spinxxxx commited on
Commit
902efd1
·
1 Parent(s): 762ebe8

feat: add issue priority prediction model (score-based)

Browse files
README.md CHANGED
@@ -1,3 +1,276 @@
1
  ---
 
 
 
 
 
 
 
 
2
  license: apache-2.0
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ language:
3
+ - ko
4
+ - en
5
+ tags:
6
+ - text-classification
7
+ - regression
8
+ - commit-priority
9
+ - issue-priority
10
  license: apache-2.0
11
+ datasets:
12
+ - custom
13
+ metrics:
14
+ - mae
15
+ - rmse
16
+ - spearman
17
  ---
18
+
19
+ # Issue Priority Predictor (Korean)
20
+
21
+ **커밋/이슈의 우선순위를 자동으로 예측하는 한국어/영어 지원 모델**
22
+
23
+ ## Model Details
24
+
25
+ 이 모델은 GitHub 커밋 텍스트를 기반으로
26
+ 우선순위 점수(priority score)를 예측하는 다국어 모델입니다.
27
+
28
+ distilbert-base-multilingual-cased를 기반으로 하여,
29
+ 한국어와 영어로 작성된 커밋 데이터를 사용해 파인튜닝되었습니다.
30
+
31
+ 모델은 입력 텍스트에 대해 0~1 범위의 연속적인 점수를 출력하며,
32
+ 점수가 높을수록 상대적으로 우선순위가 높음을 의미합니다.
33
+ 최종적인 우선순위 클래스(HIGH / MED / LOW)는
34
+ 서비스 환경에 맞는 후처리 정책을 통해 결정하는 것을 전제로 합니다.
35
+
36
+ Evaluation Metrics
37
+
38
+ 아래 평가지표는 0~1로 스케일링된 우선순위 점수를 기준으로 산출되었습니다.
39
+
40
+ Loss: 0.0045
41
+
42
+ MAE (평균 절대 오차): 0.0122
43
+
44
+ RMSE (평균 제곱근 오차): 0.0150
45
+
46
+ Spearman 상관계수: 0.8473
47
+
48
+ **Note**
49
+ 본 모델은 우선순위를 직접 분류(classification)하지 않고, 모델이 예측한 점수를 기반으로
50
+ 도메인 정책(보안, 결제, 장애, 문서 변경 등)을 반영한 후처리를 적용하도록 설계되었습니다.
51
+
52
+ ## 🚀 빠른 시작
53
+
54
+ ### 모델 예측 (점수만 출력)
55
+
56
+ ```python
57
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
58
+ import torch
59
+ import json
60
+
61
+ # 모델 로드
62
+ model_name = "your-username/issue-priority-ko"
63
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
64
+ model = AutoModelForSequenceClassification.from_pretrained(model_name)
65
+ model.eval()
66
+
67
+ # 예측 (점수만 출력)
68
+ text = "로그인 안됨, 토큰 만료 처리 필요"
69
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=256)
70
+
71
+ with torch.no_grad():
72
+ score_raw = model(**inputs).logits.item() # 0~1 범위 점수
73
+
74
+ # 원래 스케일로 복원
75
+ with open("score_thresholds.json", "r", encoding="utf-8") as f:
76
+ thresholds = json.load(f)
77
+
78
+ score = score_raw * (thresholds["train_max"] - thresholds["train_min"]) + thresholds["train_min"]
79
+
80
+ print(f"Predicted Score: {score:.4f}")
81
+ ```
82
+
83
+ ### 점수 → 클래스 변환 (후처리)
84
+
85
+ ```python
86
+ # 방법 1: to_priority 함수 사용 (권장)
87
+ from postprocess.to_priority import to_priority
88
+
89
+ # 기본 변환 (후처리 규칙 없음)
90
+ priority = to_priority(score=score, text=text)
91
+ print(f"Priority: {priority}")
92
+
93
+ # 후처리 규칙 포함 (옵션)
94
+ priority = to_priority(score=score, text=text, use_rules=True)
95
+ print(f"Priority (with rules): {priority}")
96
+ ```
97
+
98
+ ```python
99
+ # 방법 2: 직접 변환
100
+ if score >= thresholds["q_high"]:
101
+ priority = "HIGH"
102
+ elif score <= thresholds["q_low"]:
103
+ priority = "LOW"
104
+ else:
105
+ priority = "MED"
106
+ ```
107
+
108
+ ## 📋 모델 정보
109
+
110
+ | 항목 | 내용 |
111
+ |------|------|
112
+ | **기반 모델** | `distilbert-base-multilingual-cased` |
113
+ | **작업 유형** | 회귀 (Regression) |
114
+ | **입력** | 커밋/이슈 제목 + 본문 텍스트 |
115
+ | **출력** | 우선순위 점수 (float) |
116
+ | **클래스 변환** | 후처리로 수행 (`to_priority()` 함수) |
117
+ | **언어** | 한국어, 영어 |
118
+ | **최대 길이** | 256 토큰 |
119
+
120
+ > **중요**: 모델은 점수만 출력합니다. HIGH/MED/LOW 클래스 변환은 `to_priority()` 함수를 사용하세요.
121
+
122
+ ## 🎯 주요 특징
123
+
124
+ 1. **다국어 지원**: 한국어와 영어 커밋/이슈 모두 처리 가능
125
+ 2. **키워드 기반 후처리**: `postprocess/priority_rules.yaml`로 규칙 커스터마이징
126
+ 3. **배치 내 상대 정렬**: 여러 이슈를 함께 비교하여 더 정확한 우선순위 예측
127
+ 4. **경량 모델**: DistilBERT 기반으로 빠른 추론 속도
128
+
129
+ ## 📁 폴더 구조
130
+
131
+ ```
132
+ issue-priority-ko/
133
+ ├── README.md # 이 파일
134
+ ├── config.json # 모델 설정
135
+ ├── model.safetensors # 모델 가중치
136
+ ├── tokenizer.json # 토크나이저
137
+ ├── tokenizer_config.json
138
+ ├── vocab.txt
139
+ ├── score_thresholds.json # 우선순위 변환 임계값
140
+
141
+ ├── postprocess/ # 후처리 규칙 (옵션)
142
+ │ ├── to_priority.py # 점수→클래스 변환 함수
143
+ │ ├── priority_rules.yaml # 키워드 기반 규칙 (옵션)
144
+ │ └── README.md # 후처리 설명
145
+
146
+ ├── examples/ # 사용 예제
147
+ │ ├── input.json
148
+ │ └── output.json
149
+
150
+ └── requirements.txt # 의존성 패키지
151
+ ```
152
+
153
+ ## 🔄 점수 → 클래스 변환
154
+
155
+ ### `to_priority()` 함수 사용
156
+
157
+ ```python
158
+ from postprocess.to_priority import to_priority
159
+
160
+ # 기본 변환 (threshold 기반)
161
+ priority = to_priority(score=0.82, text="로그인 에러 발생")
162
+
163
+ # 후처리 규칙 포함 (옵션)
164
+ priority = to_priority(score=0.82, text="로그인 에러 발생", use_rules=True)
165
+
166
+ # 배치 변환
167
+ from postprocess.to_priority import to_priority_batch
168
+ scores = [0.82, 0.75, 0.90]
169
+ texts = ["로그인 에러", "README 수정", "서버 다운"]
170
+ priorities = to_priority_batch(scores, texts, use_rules=True)
171
+ ```
172
+
173
+ ### 후처리 규칙 (옵션)
174
+
175
+ `postprocess/priority_rules.yaml`을 사용하여 키워드 기반 규칙을 적용할 수 있습니다.
176
+
177
+ **규칙 예시:**
178
+ - **LOW 강제**: `readme`, `typo`, `문서` → 무조건 LOW
179
+ - **최소 MED 보장**: `장애`, `에러`, `로그인`, `결제` → 최소 MED
180
+ - **HIGH 부스트**: `데이터 손실`, `무한`, `critical` → HIGH
181
+
182
+ 자세한 내용은 [`postprocess/README.md`](postprocess/README.md)를 참고하세요.
183
+
184
+ ## 📊 성능 지표
185
+
186
+ | 지표 | 값 |
187
+ |------|-----|
188
+ | **MAE** | 0.009 (스케일된 값 기준) |
189
+ | **RMSE** | 0.015 (스케일된 값 기준) |
190
+ | **Spearman Correlation** | 0.85 |
191
+
192
+ > **참고**: 모델은 상대적 순위 예측에 더 적합합니다. 절대 점수보다는 배치 내 비교를 권장합니다.
193
+
194
+ ## 💡 사용 팁
195
+
196
+ ### 1. 단일 예측
197
+ ```python
198
+ # 모델 예측
199
+ text = "로그인 안됨"
200
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=256)
201
+ with torch.no_grad():
202
+ score_raw = model(**inputs).logits.item()
203
+
204
+ # 스케일 복원
205
+ score = score_raw * (thresholds["train_max"] - thresholds["train_min"]) + thresholds["train_min"]
206
+
207
+ # 클래스 변환
208
+ from postprocess.to_priority import to_priority
209
+ priority = to_priority(score=score, text=text, use_rules=True)
210
+ ```
211
+
212
+ ### 2. 배치 예측 (권장)
213
+ ```python
214
+ texts = ["이슈1", "이슈2", "이슈3"]
215
+ inputs = tokenizer(texts, return_tensors="pt", truncation=True, max_length=256, padding=True)
216
+
217
+ with torch.no_grad():
218
+ scores_raw = model(**inputs).logits.squeeze(-1).numpy()
219
+
220
+ # 스케일 복원
221
+ scores = scores_raw * (train_max - train_min) + train_min
222
+
223
+ # 배치 내 상대 정렬 (quantile 기반)
224
+ from scipy.stats import rankdata
225
+ normalized = rankdata(scores, method='average') / len(scores)
226
+
227
+ # 상위 30% = HIGH, 하위 30% = LOW
228
+ q_high = np.percentile(normalized, 70)
229
+ q_low = np.percentile(normalized, 30)
230
+ ```
231
+
232
+ ### 3. 배치 예측 + 클래스 변환
233
+ ```python
234
+ # 배치 예측
235
+ texts = ["이슈1", "이슈2", "이슈3"]
236
+ inputs = tokenizer(texts, return_tensors="pt", truncation=True, max_length=256, padding=True)
237
+
238
+ with torch.no_grad():
239
+ scores_raw = model(**inputs).logits.squeeze(-1).numpy()
240
+
241
+ # 스케일 복원
242
+ scores = scores_raw * (thresholds["train_max"] - thresholds["train_min"]) + thresholds["train_min"]
243
+
244
+ # 배치 클래스 변환
245
+ from postprocess.to_priority import to_priority_batch
246
+ priorities = to_priority_batch(scores, texts, use_rules=True)
247
+
248
+ for text, score, priority in zip(texts, scores, priorities):
249
+ print(f"{priority}: {score:.4f} - {text}")
250
+ ```
251
+
252
+ ## ⚠️ 주의사항
253
+
254
+ 1. **모델 출력**: 모델은 점수만 출력합니다 (회귀 모델). 클래스 변환은 `to_priority()` 함수 사용
255
+ 2. **스케일 복원 필수**: 모델 출력은 0~1 범위입니다. `score_thresholds.json`으로 원래 스케일 복원 필요
256
+ 3. **상대적 순위**: 절대 점수보다는 배치 내 상대 비교가 더 정확
257
+ 4. **후처리 규칙**: `priority_rules.yaml`은 옵션입니다. 필요시에만 사용
258
+ 5. **도메인 적응**: 새로운 도메인에서는 재학습 또는 파인튜닝 권장
259
+
260
+ ## 📚 예제
261
+
262
+ 실제 사용 예제는 [`examples/`](examples/) 폴더를 참고하세요.
263
+
264
+ - `input.json`: 입력 예제
265
+ - `output.json`: 출력 예제
266
+
267
+ ## 🔗 관련 자료
268
+
269
+ - **변환 함수**: [`postprocess/to_priority.py`](postprocess/to_priority.py) - 점수→클래스 변환
270
+ - **후처리 규칙 (옵션)**: [`postprocess/priority_rules.yaml`](postprocess/priority_rules.yaml)
271
+ - **후처리 설명**: [`postprocess/README.md`](postprocess/README.md)
272
+
273
+ ## 📄 라이센스
274
+
275
+ - Apache 2.0
276
+
added_tokens.json ADDED
@@ -0,0 +1,700 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "<commit_type_add>": 119558,
3
+ "<commit_type_build>": 119550,
4
+ "<commit_type_change>": 119560,
5
+ "<commit_type_changelog>": 119588,
6
+ "<commit_type_checkoutpr>": 119569,
7
+ "<commit_type_chore>": 119548,
8
+ "<commit_type_ci>": 119563,
9
+ "<commit_type_citavi>": 119575,
10
+ "<commit_type_cleanup>": 119581,
11
+ "<commit_type_code>": 119591,
12
+ "<commit_type_debug>": 119573,
13
+ "<commit_type_docs>": 119555,
14
+ "<commit_type_feat>": 119552,
15
+ "<commit_type_feature>": 119566,
16
+ "<commit_type_fix>": 119547,
17
+ "<commit_type_flatpack>": 119595,
18
+ "<commit_type_ghprcomment>": 119574,
19
+ "<commit_type_gitbook>": 119582,
20
+ "<commit_type_gitversion>": 119594,
21
+ "<commit_type_group>": 119587,
22
+ "<commit_type_groups>": 119589,
23
+ "<commit_type_gui>": 119577,
24
+ "<commit_type_hotfix>": 119567,
25
+ "<commit_type_identifier>": 119572,
26
+ "<commit_type_improve>": 119561,
27
+ "<commit_type_issue>": 119583,
28
+ "<commit_type_journallist>": 119592,
29
+ "<commit_type_modify>": 119564,
30
+ "<commit_type_mysql>": 119593,
31
+ "<commit_type_perf>": 119562,
32
+ "<commit_type_pipeline>": 119578,
33
+ "<commit_type_preapre>": 119568,
34
+ "<commit_type_preferences>": 119570,
35
+ "<commit_type_readme>": 119565,
36
+ "<commit_type_refactor>": 119549,
37
+ "<commit_type_remove>": 119556,
38
+ "<commit_type_rename>": 119576,
39
+ "<commit_type_revert>": 119557,
40
+ "<commit_type_reviewdoc>": 119580,
41
+ "<commit_type_snap>": 119584,
42
+ "<commit_type_snapcraft>": 119586,
43
+ "<commit_type_style>": 119554,
44
+ "<commit_type_test>": 119553,
45
+ "<commit_type_typo>": 119579,
46
+ "<commit_type_ui>": 119585,
47
+ "<commit_type_unknown>": 119596,
48
+ "<commit_type_update>": 119559,
49
+ "<commit_type_upgrade>": 119551,
50
+ "<commit_type_ux>": 119590,
51
+ "<commit_type_wip>": 119571,
52
+ "<scope_#10176>": 119850,
53
+ "<scope_#10721>": 119855,
54
+ "<scope_#11033>": 119857,
55
+ "<scope_#11191>": 119859,
56
+ "<scope_#1714>": 119875,
57
+ "<scope_#1962>": 119873,
58
+ "<scope_#2234>": 119864,
59
+ "<scope_#8852>": 119860,
60
+ "<scope_#9774>": 119858,
61
+ "<scope_8448>": 120235,
62
+ "<scope_API>": 119656,
63
+ "<scope_AU-1855>": 120208,
64
+ "<scope_AU-1950>": 120206,
65
+ "<scope_AU-2042>": 120205,
66
+ "<scope_AU-2128>": 120204,
67
+ "<scope_AU-2269>": 120203,
68
+ "<scope_AV1 DD>": 119870,
69
+ "<scope_AV1>": 119866,
70
+ "<scope_Accessibility>": 120175,
71
+ "<scope_AddonUserProfile>": 120083,
72
+ "<scope_Behat>": 120181,
73
+ "<scope_Book>": 120092,
74
+ "<scope_ChannelShim>": 119876,
75
+ "<scope_Comment>": 120237,
76
+ "<scope_ComponentImpl>": 119886,
77
+ "<scope_Conference>": 119892,
78
+ "<scope_ConferenceSpeechActivity>": 119897,
79
+ "<scope_Config>": 120089,
80
+ "<scope_Content>": 119894,
81
+ "<scope_ControllerTest>": 120239,
82
+ "<scope_Courses>": 120176,
83
+ "<scope_Credentials>": 120085,
84
+ "<scope_DSpace#10176>": 119849,
85
+ "<scope_DX>": 120125,
86
+ "<scope_Dashboard>": 119791,
87
+ "<scope_Dev>": 120088,
88
+ "<scope_Docker>": 120146,
89
+ "<scope_DuplicateDetectionIT>": 119851,
90
+ "<scope_EndpointConnectionStatus>": 119882,
91
+ "<scope_Enterprise>": 120224,
92
+ "<scope_FS>": 119817,
93
+ "<scope_HTML5>": 119665,
94
+ "<scope_JMT>": 119877,
95
+ "<scope_LAD>": 119628,
96
+ "<scope_MediaSourceDesc.copy>": 119879,
97
+ "<scope_MediaSourceDesc>": 119880,
98
+ "<scope_Poll>": 119669,
99
+ "<scope_PostAcceptanceTest>": 120238,
100
+ "<scope_PostRepository>": 120241,
101
+ "<scope_PostServiceTest>": 120240,
102
+ "<scope_ReportBuilder>": 120090,
103
+ "<scope_SSRC rewriting>": 119865,
104
+ "<scope_Site>": 120087,
105
+ "<scope_UI>": 120119,
106
+ "<scope_UX>": 120135,
107
+ "<scope_UsernameUniqueResponse>": 120242,
108
+ "<scope_VideoParser>": 119871,
109
+ "<scope_Videobridge>": 119893,
110
+ "<scope_WebRtcDataStream>": 119883,
111
+ "<scope_a11y>": 119920,
112
+ "<scope_about>": 119973,
113
+ "<scope_acceptances>": 120044,
114
+ "<scope_accessibility>": 120163,
115
+ "<scope_action>": 120077,
116
+ "<scope_actions-bar/reactions-button>": 119692,
117
+ "<scope_actions-bar>": 119679,
118
+ "<scope_activity-check>": 119703,
119
+ "<scope_activity>": 120071,
120
+ "<scope_activitymodules>": 120070,
121
+ "<scope_add additional ORA submission zip logging>": 120219,
122
+ "<scope_add teams config service to previewmodulesystem>": 120218,
123
+ "<scope_addon>": 120172,
124
+ "<scope_addons>": 120049,
125
+ "<scope_adrs>": 120230,
126
+ "<scope_ai>": 120227,
127
+ "<scope_airnotifier>": 120096,
128
+ "<scope_akka-bbb-apps>": 119633,
129
+ "<scope_akka/bbb-web>": 119631,
130
+ "<scope_akka>": 119641,
131
+ "<scope_alert>": 120161,
132
+ "<scope_alerts>": 119965,
133
+ "<scope_android>": 119923,
134
+ "<scope_angular>": 119939,
135
+ "<scope_aot>": 120196,
136
+ "<scope_api>": 119822,
137
+ "<scope_app>": 120059,
138
+ "<scope_artifacts>": 119636,
139
+ "<scope_assets>": 120095,
140
+ "<scope_assign>": 119908,
141
+ "<scope_assignment>": 120108,
142
+ "<scope_async-deletion>": 119856,
143
+ "<scope_attachments>": 120039,
144
+ "<scope_audio modal>": 119758,
145
+ "<scope_audio-captions>": 119639,
146
+ "<scope_audio-controls>": 119693,
147
+ "<scope_audio/captions>": 119697,
148
+ "<scope_audio/muteToggle>": 119725,
149
+ "<scope_audio>": 119613,
150
+ "<scope_auth>": 120069,
151
+ "<scope_authentication-method>": 119848,
152
+ "<scope_autologout>": 120021,
153
+ "<scope_avatar>": 120114,
154
+ "<scope_badge>": 120005,
155
+ "<scope_badges>": 119921,
156
+ "<scope_bbb-common-web>": 119643,
157
+ "<scope_bbb-conf>": 119687,
158
+ "<scope_bbb-export-annotations>": 119620,
159
+ "<scope_bbb-graphql-server>": 119629,
160
+ "<scope_bbb-html5>": 119688,
161
+ "<scope_bbb-learning-dashboard>": 119630,
162
+ "<scope_bbb-lti>": 119814,
163
+ "<scope_bbb-pads>": 119788,
164
+ "<scope_bbb-presentation-video>": 119786,
165
+ "<scope_bbb-record-core>": 119811,
166
+ "<scope_bbb-record>": 119671,
167
+ "<scope_bbb-transcription-controller>": 119769,
168
+ "<scope_bbb-web and html5>": 119738,
169
+ "<scope_bbb-web>": 119611,
170
+ "<scope_bbb-webhooks>": 119625,
171
+ "<scope_bbb-webrtc-recorder>": 119602,
172
+ "<scope_bbb-webrtc-sfu>": 119599,
173
+ "<scope_bbb>": 119991,
174
+ "<scope_behat>": 119907,
175
+ "<scope_block>": 119944,
176
+ "<scope_block_myoverview>": 120171,
177
+ "<scope_block_tags>": 120055,
178
+ "<scope_blocks>": 119932,
179
+ "<scope_blog>": 119989,
180
+ "<scope_blogs>": 120174,
181
+ "<scope_book>": 120041,
182
+ "<scope_boostrap>": 119949,
183
+ "<scope_bootstrap>": 119911,
184
+ "<scope_bot>": 119705,
185
+ "<scope_breakout rooms>": 119787,
186
+ "<scope_breakout-room/create-breakout-room>": 119684,
187
+ "<scope_breakout-room>": 119706,
188
+ "<scope_breakout>": 119682,
189
+ "<scope_breakouts>": 119820,
190
+ "<scope_browser>": 120153,
191
+ "<scope_build>": 119770,
192
+ "<scope_bump ora to 3.6.11>": 120216,
193
+ "<scope_bump ora to 3.6.15>": 120215,
194
+ "<scope_button>": 119818,
195
+ "<scope_buttons>": 120142,
196
+ "<scope_calendar>": 119930,
197
+ "<scope_camera as content>": 119778,
198
+ "<scope_camera>": 119796,
199
+ "<scope_captcha>": 120140,
200
+ "<scope_captions/speech>": 119696,
201
+ "<scope_captions>": 119623,
202
+ "<scope_capture>": 120109,
203
+ "<scope_catchAll>": 119853,
204
+ "<scope_channel>": 119898,
205
+ "<scope_chart>": 119990,
206
+ "<scope_chat notification>": 119802,
207
+ "<scope_chat/lockViewers>": 119653,
208
+ "<scope_chat/message-content>": 119700,
209
+ "<scope_chat/message-header>": 119728,
210
+ "<scope_chat/message-toolbar>": 119699,
211
+ "<scope_chat/message>": 119694,
212
+ "<scope_chat>": 119612,
213
+ "<scope_choice>": 119976,
214
+ "<scope_chore>": 119647,
215
+ "<scope_ci-cd>": 120233,
216
+ "<scope_ci-note>": 119714,
217
+ "<scope_ci>": 119627,
218
+ "<scope_citation>": 120226,
219
+ "<scope_classes>": 120157,
220
+ "<scope_click-outside>": 119803,
221
+ "<scope_client>": 119634,
222
+ "<scope_closed captions>": 119832,
223
+ "<scope_code-review>": 119771,
224
+ "<scope_colibri>": 119884,
225
+ "<scope_collapsible>": 119945,
226
+ "<scope_combobox>": 119995,
227
+ "<scope_comments>": 119972,
228
+ "<scope_common-web>": 119638,
229
+ "<scope_compentecy>": 120188,
230
+ "<scope_competencies>": 119922,
231
+ "<scope_competency>": 120050,
232
+ "<scope_compile>": 120019,
233
+ "<scope_completion>": 119977,
234
+ "<scope_components>": 120147,
235
+ "<scope_concurrency>": 119891,
236
+ "<scope_conf>": 119711,
237
+ "<scope_conference>": 119900,
238
+ "<scope_config/settings>": 119713,
239
+ "<scope_config>": 119760,
240
+ "<scope_configs>": 120104,
241
+ "<scope_connection status modal>": 119798,
242
+ "<scope_connection status>": 119757,
243
+ "<scope_connection-status>": 119717,
244
+ "<scope_connection>": 119724,
245
+ "<scope_contentlinks>": 120152,
246
+ "<scope_cordova>": 119948,
247
+ "<scope_core-html5>": 119744,
248
+ "<scope_core>": 119607,
249
+ "<scope_course>": 119906,
250
+ "<scope_courseoverview>": 120120,
251
+ "<scope_courses>": 119926,
252
+ "<scope_credentials>": 120073,
253
+ "<scope_cron>": 120101,
254
+ "<scope_cursor>": 119785,
255
+ "<scope_dark mode>": 119789,
256
+ "<scope_dark-logo>": 119702,
257
+ "<scope_dark-theme>": 119727,
258
+ "<scope_dark>": 120138,
259
+ "<scope_darklogo>": 119731,
260
+ "<scope_darkmode>": 119730,
261
+ "<scope_dashboard>": 119733,
262
+ "<scope_data>": 119962,
263
+ "<scope_database>": 119984,
264
+ "<scope_dataprivacy>": 120046,
265
+ "<scope_date>": 120186,
266
+ "<scope_datetime>": 120048,
267
+ "<scope_db>": 120015,
268
+ "<scope_dcsctp>": 119868,
269
+ "<scope_ddmarker>": 120026,
270
+ "<scope_deb>": 119863,
271
+ "<scope_debian>": 119862,
272
+ "<scope_debounce>": 119746,
273
+ "<scope_delegate>": 120001,
274
+ "<scope_delegates>": 120047,
275
+ "<scope_dependabot>": 119869,
276
+ "<scope_dependencies>": 120107,
277
+ "<scope_deps-dev>": 119644,
278
+ "<scope_deps>": 119608,
279
+ "<scope_design>": 120023,
280
+ "<scope_desktop>": 120167,
281
+ "<scope_dev>": 120063,
282
+ "<scope_devtools>": 120103,
283
+ "<scope_directive>": 120093,
284
+ "<scope_directives>": 120155,
285
+ "<scope_disabledFeatures>": 119775,
286
+ "<scope_discussions>": 120217,
287
+ "<scope_doc>": 120173,
288
+ "<scope_docker>": 119928,
289
+ "<scope_docs>": 119663,
290
+ "<scope_dom>": 120112,
291
+ "<scope_dspace-oai>": 119852,
292
+ "<scope_dx>": 120065,
293
+ "<scope_e2e>": 120102,
294
+ "<scope_echo-test>": 119741,
295
+ "<scope_edit>": 120080,
296
+ "<scope_editor>": 120078,
297
+ "<scope_electron>": 120154,
298
+ "<scope_emoji-picker>": 119735,
299
+ "<scope_emulator>": 119961,
300
+ "<scope_enrol>": 120000,
301
+ "<scope_env>": 120144,
302
+ "<scope_error>": 120195,
303
+ "<scope_eslint>": 119958,
304
+ "<scope_essay>": 120151,
305
+ "<scope_etherpad>": 119680,
306
+ "<scope_events>": 119743,
307
+ "<scope_export-ann>": 119766,
308
+ "<scope_export-annotations>": 119761,
309
+ "<scope_extensions>": 120164,
310
+ "<scope_external video>": 119844,
311
+ "<scope_external videos>": 119793,
312
+ "<scope_external-video-player>": 119698,
313
+ "<scope_external-video>": 119708,
314
+ "<scope_external-videos>": 119824,
315
+ "<scope_extra file>": 120210,
316
+ "<scope_feedback>": 119966,
317
+ "<scope_file>": 119987,
318
+ "<scope_filepool>": 120016,
319
+ "<scope_files>": 120110,
320
+ "<scope_fileuploader>": 120053,
321
+ "<scope_filter>": 119986,
322
+ "<scope_filters>": 120170,
323
+ "<scope_firebase>": 120165,
324
+ "<scope_fix>": 119616,
325
+ "<scope_focus>": 120234,
326
+ "<scope_folder>": 120128,
327
+ "<scope_fontawesome>": 120200,
328
+ "<scope_fonts>": 120086,
329
+ "<scope_form>": 120054,
330
+ "<scope_formattext>": 120134,
331
+ "<scope_forum>": 119935,
332
+ "<scope_freeswitch>": 119657,
333
+ "<scope_fsesl-akka>": 119801,
334
+ "<scope_fullaudio>": 119813,
335
+ "<scope_fullscreen>": 120117,
336
+ "<scope_generic-component>": 119748,
337
+ "<scope_generic-content>": 119747,
338
+ "<scope_geolocation>": 120091,
339
+ "<scope_gha>": 119909,
340
+ "<scope_git>": 120191,
341
+ "<scope_github>": 119946,
342
+ "<scope_gladia>": 119759,
343
+ "<scope_globalization>": 120194,
344
+ "<scope_glossary>": 119963,
345
+ "<scope_gql-actions>": 119677,
346
+ "<scope_gql>": 119740,
347
+ "<scope_grades>": 119988,
348
+ "<scope_graphql-actions>": 119675,
349
+ "<scope_graphql-server>": 119598,
350
+ "<scope_graphql>": 119768,
351
+ "<scope_greenlight docs>": 119773,
352
+ "<scope_group>": 120040,
353
+ "<scope_groups>": 120116,
354
+ "<scope_guest wait>": 119828,
355
+ "<scope_guest-wait>": 119750,
356
+ "<scope_guest-waiting>": 119666,
357
+ "<scope_guest>": 119815,
358
+ "<scope_guests>": 119839,
359
+ "<scope_gui>": 120231,
360
+ "<scope_gulp>": 120056,
361
+ "<scope_h5p>": 119929,
362
+ "<scope_h5pactivity>": 119912,
363
+ "<scope_home>": 119993,
364
+ "<scope_html5, akka>": 119664,
365
+ "<scope_html5,akka>": 119635,
366
+ "<scope_html5-prom>": 119830,
367
+ "<scope_html5-prometheus>": 119831,
368
+ "<scope_html5-server>": 119829,
369
+ "<scope_html5/chat>": 119618,
370
+ "<scope_html5/external-video>": 119650,
371
+ "<scope_html5/layout>": 119651,
372
+ "<scope_html5/plugin>": 119654,
373
+ "<scope_html5/poll>": 119648,
374
+ "<scope_html5/user-list>": 119605,
375
+ "<scope_html5/voice-activity>": 119617,
376
+ "<scope_html5/whiteboard>": 119619,
377
+ "<scope_html5>": 119597,
378
+ "<scope_html5|external-video>": 119655,
379
+ "<scope_html>": 119668,
380
+ "<scope_http>": 120131,
381
+ "<scope_i18n>": 120209,
382
+ "<scope_iab>": 120017,
383
+ "<scope_ice4j>": 119872,
384
+ "<scope_icon>": 119776,
385
+ "<scope_icons>": 119736,
386
+ "<scope_iframe>": 119974,
387
+ "<scope_iframes>": 120030,
388
+ "<scope_imscp>": 120020,
389
+ "<scope_inappbrowser>": 120130,
390
+ "<scope_install>": 120177,
391
+ "<scope_interactions-button>": 119781,
392
+ "<scope_interactions>": 119780,
393
+ "<scope_ionic>": 119940,
394
+ "<scope_ios>": 119992,
395
+ "<scope_items>": 120127,
396
+ "<scope_jest>": 119960,
397
+ "<scope_jsinput>": 120220,
398
+ "<scope_junie>": 120232,
399
+ "<scope_keyboard>": 119954,
400
+ "<scope_label>": 120068,
401
+ "<scope_lang>": 119903,
402
+ "<scope_language>": 120168,
403
+ "<scope_layout-manager>": 119767,
404
+ "<scope_layout-modal>": 119676,
405
+ "<scope_layout/modal>": 119712,
406
+ "<scope_layout/observer>": 119719,
407
+ "<scope_layout>": 119642,
408
+ "<scope_layouts>": 119670,
409
+ "<scope_learning-dashboard>": 119701,
410
+ "<scope_lesson>": 119931,
411
+ "<scope_libs>": 120169,
412
+ "<scope_licenses>": 120032,
413
+ "<scope_like>": 120236,
414
+ "<scope_link>": 119947,
415
+ "<scope_links>": 120002,
416
+ "<scope_lint>": 119797,
417
+ "<scope_linux>": 120193,
418
+ "<scope_listen-only>": 119819,
419
+ "<scope_livekit>": 119600,
420
+ "<scope_livekt>": 119609,
421
+ "<scope_lms and studio>": 120223,
422
+ "<scope_lms>": 120222,
423
+ "<scope_loading>": 119937,
424
+ "<scope_locale>": 119792,
425
+ "<scope_locales>": 119843,
426
+ "<scope_localnotif>": 120184,
427
+ "<scope_localnotifications>": 120011,
428
+ "<scope_location>": 120024,
429
+ "<scope_lock viewers>": 119751,
430
+ "<scope_locutus>": 120060,
431
+ "<scope_log>": 119867,
432
+ "<scope_logging>": 119890,
433
+ "<scope_login>": 119916,
434
+ "<scope_logo>": 119840,
435
+ "<scope_logout>": 119729,
436
+ "<scope_ls-hack>": 119885,
437
+ "<scope_lshack>": 119887,
438
+ "<scope_lti>": 120160,
439
+ "<scope_mac>": 120183,
440
+ "<scope_main>": 120118,
441
+ "<scope_mainmenu>": 119979,
442
+ "<scope_mathjax>": 119913,
443
+ "<scope_media>": 119952,
444
+ "<scope_meeting-ended/rating>": 119715,
445
+ "<scope_meeting-ended>": 119691,
446
+ "<scope_meeting>": 119825,
447
+ "<scope_menu>": 119936,
448
+ "<scope_merge-dialog>": 120229,
449
+ "<scope_message-form>": 119800,
450
+ "<scope_message>": 120018,
451
+ "<scope_messages>": 119917,
452
+ "<scope_microphone>": 119808,
453
+ "<scope_middleware>": 119660,
454
+ "<scope_migration>": 120137,
455
+ "<scope_mimetype>": 120045,
456
+ "<scope_minor>": 119895,
457
+ "<scope_mobile_api>": 120207,
458
+ "<scope_mod>": 119971,
459
+ "<scope_mod_data>": 120025,
460
+ "<scope_mod_url>": 119957,
461
+ "<scope_modal/confirmation>": 119726,
462
+ "<scope_modal>": 119718,
463
+ "<scope_modals>": 120037,
464
+ "<scope_modicon>": 120036,
465
+ "<scope_module>": 119951,
466
+ "<scope_modules>": 119983,
467
+ "<scope_more>": 120122,
468
+ "<scope_multi-stream>": 119874,
469
+ "<scope_multilang>": 120081,
470
+ "<scope_my>": 120111,
471
+ "<scope_mycourses>": 120067,
472
+ "<scope_myovervew>": 120123,
473
+ "<scope_myoverview>": 119950,
474
+ "<scope_native>": 120062,
475
+ "<scope_nav-bar/leave-meeting-button>": 119681,
476
+ "<scope_nav-bar/recording-indicator>": 119709,
477
+ "<scope_nav-bar/talking-indicator>": 119710,
478
+ "<scope_navbar>": 120115,
479
+ "<scope_navigation>": 120132,
480
+ "<scope_navigator>": 120124,
481
+ "<scope_network>": 119955,
482
+ "<scope_ng>": 120061,
483
+ "<scope_notes>": 119690,
484
+ "<scope_notification>": 119678,
485
+ "<scope_notifications>": 119956,
486
+ "<scope_npm>": 119985,
487
+ "<scope_numericalInput>": 120202,
488
+ "<scope_offline>": 120178,
489
+ "<scope_ogvjs>": 120076,
490
+ "<scope_ora>": 120221,
491
+ "<scope_other>": 120166,
492
+ "<scope_overlays>": 120038,
493
+ "<scope_overview>": 119933,
494
+ "<scope_package>": 119959,
495
+ "<scope_pads>": 119764,
496
+ "<scope_page>": 119975,
497
+ "<scope_pantool>": 119784,
498
+ "<scope_participants>": 120079,
499
+ "<scope_password>": 120029,
500
+ "<scope_peertube>": 119837,
501
+ "<scope_perf>": 119745,
502
+ "<scope_performance>": 120074,
503
+ "<scope_pin-notes>": 119662,
504
+ "<scope_pipes>": 120156,
505
+ "<scope_playback>": 119794,
506
+ "<scope_plugin-sdk>": 119649,
507
+ "<scope_plugin/html5>": 119659,
508
+ "<scope_plugin>": 119601,
509
+ "<scope_pluginfile>": 120022,
510
+ "<scope_plugins-sdk>": 119614,
511
+ "<scope_plugins>": 119674,
512
+ "<scope_policy>": 119914,
513
+ "<scope_poll annotations>": 119763,
514
+ "<scope_poll>": 119603,
515
+ "<scope_polling>": 119765,
516
+ "<scope_polls>": 119707,
517
+ "<scope_popover>": 120027,
518
+ "<scope_popovers>": 119942,
519
+ "<scope_pr-automerge-open-release>": 120212,
520
+ "<scope_prefetch>": 120187,
521
+ "<scope_presentation conversion>": 119790,
522
+ "<scope_presentation-menu>": 119685,
523
+ "<scope_presentation-toast>": 119723,
524
+ "<scope_presentation-toolbar>": 119834,
525
+ "<scope_presentation>": 119667,
526
+ "<scope_private-chat>": 119737,
527
+ "<scope_privatefiles>": 120007,
528
+ "<scope_process_warnings>": 120211,
529
+ "<scope_proctoring>": 120214,
530
+ "<scope_profile>": 120075,
531
+ "<scope_program-portals>": 120213,
532
+ "<scope_progressbar>": 120064,
533
+ "<scope_prom-html5>": 119827,
534
+ "<scope_push>": 119970,
535
+ "<scope_pushnotifications>": 120012,
536
+ "<scope_qr>": 120162,
537
+ "<scope_qrscanner>": 119941,
538
+ "<scope_qtype>": 120009,
539
+ "<scope_qtype_match>": 120033,
540
+ "<scope_question>": 119996,
541
+ "<scope_quick-poll>": 119672,
542
+ "<scope_quiz>": 119910,
543
+ "<scope_rating>": 120182,
544
+ "<scope_ratings>": 120179,
545
+ "<scope_reaction-button>": 119673,
546
+ "<scope_reactions-button>": 119716,
547
+ "<scope_reactions>": 119756,
548
+ "<scope_reading>": 119964,
549
+ "<scope_readme>": 120126,
550
+ "<scope_recaptcha>": 120139,
551
+ "<scope_recentcourses>": 120190,
552
+ "<scope_recentlyaccesseditems>": 119967,
553
+ "<scope_recipients>": 119854,
554
+ "<scope_reconnect>": 119783,
555
+ "<scope_reconnection>": 119777,
556
+ "<scope_record-and-playback>": 119826,
557
+ "<scope_record>": 119845,
558
+ "<scope_recording>": 119610,
559
+ "<scope_red>": 119878,
560
+ "<scope_refactor>": 119652,
561
+ "<scope_refresh>": 120031,
562
+ "<scope_release>": 120094,
563
+ "<scope_reminders>": 120052,
564
+ "<scope_remotethemes>": 120013,
565
+ "<scope_renotethemes>": 120197,
566
+ "<scope_reportbuilder>": 120072,
567
+ "<scope_reports>": 120006,
568
+ "<scope_resource>": 119953,
569
+ "<scope_resources>": 119896,
570
+ "<scope_routes>": 120150,
571
+ "<scope_routing>": 120149,
572
+ "<scope_rte>": 119934,
573
+ "<scope_rtl>": 120004,
574
+ "<scope_rtp>": 119901,
575
+ "<scope_rxjs>": 120100,
576
+ "<scope_sbom>": 120225,
577
+ "<scope_scorm>": 119999,
578
+ "<scope_screen-reader>": 119755,
579
+ "<scope_screenshare>": 119646,
580
+ "<scope_screnshare>": 119645,
581
+ "<scope_scripts>": 120028,
582
+ "<scope_search>": 120003,
583
+ "<scope_sec>": 119606,
584
+ "<scope_security>": 119846,
585
+ "<scope_services>": 120158,
586
+ "<scope_session-details>": 119658,
587
+ "<scope_settings/service>": 119721,
588
+ "<scope_settings>": 119752,
589
+ "<scope_sfu>": 119842,
590
+ "<scope_shared notes>": 119833,
591
+ "<scope_shared-notes>": 119661,
592
+ "<scope_sharedNotes>": 119734,
593
+ "<scope_sharedfiles>": 120051,
594
+ "<scope_shib-auth-groups>": 119847,
595
+ "<scope_sidebar>": 119689,
596
+ "<scope_signup>": 120057,
597
+ "<scope_sim>": 119888,
598
+ "<scope_simulcast>": 119889,
599
+ "<scope_singletons>": 120099,
600
+ "<scope_site>": 119905,
601
+ "<scope_siteaddons>": 120201,
602
+ "<scope_sitehome>": 119981,
603
+ "<scope_sitemenu>": 120192,
604
+ "<scope_siteplugins>": 120066,
605
+ "<scope_sites>": 119978,
606
+ "<scope_slides>": 119969,
607
+ "<scope_slr>": 120228,
608
+ "<scope_sound>": 119622,
609
+ "<scope_splash>": 120129,
610
+ "<scope_splashscreen>": 120198,
611
+ "<scope_split>": 120180,
612
+ "<scope_splitview>": 120143,
613
+ "<scope_sqlite>": 119982,
614
+ "<scope_starredcourses>": 120189,
615
+ "<scope_stats>": 119899,
616
+ "<scope_statusbar>": 120141,
617
+ "<scope_storage>": 119749,
618
+ "<scope_storagemanager>": 119997,
619
+ "<scope_streaming>": 119704,
620
+ "<scope_strings>": 120159,
621
+ "<scope_style>": 119823,
622
+ "<scope_styles>": 119720,
623
+ "<scope_submission>": 119861,
624
+ "<scope_subscriptions>": 119809,
625
+ "<scope_survey>": 119927,
626
+ "<scope_swipe>": 120058,
627
+ "<scope_swiper>": 119925,
628
+ "<scope_sync>": 120084,
629
+ "<scope_systemd>": 119881,
630
+ "<scope_tabs>": 120008,
631
+ "<scope_tag>": 119918,
632
+ "<scope_tags>": 119968,
633
+ "<scope_test/chat>": 119640,
634
+ "<scope_test>": 119637,
635
+ "<scope_testing>": 120098,
636
+ "<scope_tests>": 119615,
637
+ "<scope_text>": 120145,
638
+ "<scope_theme>": 119810,
639
+ "<scope_time>": 120097,
640
+ "<scope_timeline>": 119943,
641
+ "<scope_timer>": 119722,
642
+ "<scope_tldraw>": 119604,
643
+ "<scope_toast>": 119686,
644
+ "<scope_tooltip>": 119695,
645
+ "<scope_tours>": 120106,
646
+ "<scope_transcription>": 119782,
647
+ "<scope_travis>": 119980,
648
+ "<scope_ts>": 120133,
649
+ "<scope_tsconfig>": 120199,
650
+ "<scope_types>": 120043,
651
+ "<scope_typescript>": 119938,
652
+ "<scope_typings>": 120148,
653
+ "<scope_ui>": 120035,
654
+ "<scope_upgrade>": 120042,
655
+ "<scope_upload>": 120136,
656
+ "<scope_url>": 120010,
657
+ "<scope_urlschemes>": 120105,
658
+ "<scope_user-actions>": 119779,
659
+ "<scope_user-avatar>": 119805,
660
+ "<scope_user-list>": 119762,
661
+ "<scope_user-status>": 119807,
662
+ "<scope_user>": 119816,
663
+ "<scope_userdata>": 119821,
664
+ "<scope_usermenu>": 120121,
665
+ "<scope_userprofile>": 120082,
666
+ "<scope_users>": 119753,
667
+ "<scope_usertours>": 120113,
668
+ "<scope_utils>": 120014,
669
+ "<scope_ux>": 119998,
670
+ "<scope_video-avatar>": 119799,
671
+ "<scope_video-list-item>": 119806,
672
+ "<scope_video-preview>": 119632,
673
+ "<scope_video>": 119774,
674
+ "<scope_videochannel>": 119902,
675
+ "<scope_viewer>": 120034,
676
+ "<scope_vimeo>": 119836,
677
+ "<scope_virtual-background>": 119624,
678
+ "<scope_virtual-backgrounds>": 119626,
679
+ "<scope_voice>": 119732,
680
+ "<scope_volume-slider>": 119838,
681
+ "<scope_vscode>": 119994,
682
+ "<scope_waiting-users>": 119812,
683
+ "<scope_wake lock>": 119772,
684
+ "<scope_wake-lock>": 119754,
685
+ "<scope_wb>": 119742,
686
+ "<scope_web>": 119841,
687
+ "<scope_webcam>": 119683,
688
+ "<scope_webcams>": 119739,
689
+ "<scope_webhooks>": 119804,
690
+ "<scope_webrtc>": 119835,
691
+ "<scope_whiteboard toolbar>": 119795,
692
+ "<scope_whiteboard>": 119621,
693
+ "<scope_wiki>": 119919,
694
+ "<scope_windows>": 120185,
695
+ "<scope_workshop>": 119915,
696
+ "<scope_ws>": 119904,
697
+ "<scope_youtube>": 119924,
698
+ "[high_impact]": 120243,
699
+ "[low_impact]": 120244
700
+ }
config.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "activation": "gelu",
3
+ "architectures": [
4
+ "DistilBertForSequenceClassification"
5
+ ],
6
+ "attention_dropout": 0.1,
7
+ "dim": 768,
8
+ "dropout": 0.1,
9
+ "dtype": "float32",
10
+ "hidden_dim": 3072,
11
+ "id2label": {
12
+ "0": "LABEL_0"
13
+ },
14
+ "initializer_range": 0.02,
15
+ "label2id": {
16
+ "LABEL_0": 0
17
+ },
18
+ "max_position_embeddings": 512,
19
+ "model_type": "distilbert",
20
+ "n_heads": 12,
21
+ "n_layers": 6,
22
+ "output_past": true,
23
+ "pad_token_id": 0,
24
+ "problem_type": "regression",
25
+ "qa_dropout": 0.1,
26
+ "seq_classif_dropout": 0.2,
27
+ "sinusoidal_pos_embds": false,
28
+ "tie_weights_": true,
29
+ "transformers_version": "4.57.6",
30
+ "vocab_size": 119547
31
+ }
examples/input.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "subject": "로그인 안됨, 토큰 만료 처리 필요",
4
+ "body": "사용자가 로그인 시 401 에러가 발생합니다. JWT 토큰 만료 처리가 필요합니다."
5
+ },
6
+ {
7
+ "subject": "README 오타 수정",
8
+ "body": "README.md 파일의 오타를 수정했습니다."
9
+ },
10
+ {
11
+ "subject": "서버 다운 문제 발생",
12
+ "body": "프로덕션 서버가 다운되었습니다. 긴급 조치가 필요합니다."
13
+ },
14
+ {
15
+ "subject": "데이터 손실 발생",
16
+ "body": "데이터베이스 백업 중 데이터 손실이 발생했습니다. critical 이슈입니다."
17
+ },
18
+ {
19
+ "subject": "UI 개선 제안",
20
+ "body": "사용자 인터페이스를 개선하는 제안입니다."
21
+ }
22
+ ]
examples/output.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "subject": "로그인 안됨, 토큰 만료 처리 필요",
4
+ "body": "사용자가 로그인 시 401 에러가 발생합니다. JWT 토큰 만료 처리가 필요합니다.",
5
+ "predicted_score": 0.8234,
6
+ "predicted_priority": "HIGH",
7
+ "original_index": 0
8
+ },
9
+ {
10
+ "subject": "서버 다운 문제 발생",
11
+ "body": "프로덕션 서버가 다운되었습니다. 긴급 조치가 필요합니다.",
12
+ "predicted_score": 0.8156,
13
+ "predicted_priority": "HIGH",
14
+ "original_index": 2
15
+ },
16
+ {
17
+ "subject": "데이터 손실 발생",
18
+ "body": "데이터베이스 백업 중 데이터 손실이 발생했습니다. critical 이슈입니다.",
19
+ "predicted_score": 0.8123,
20
+ "predicted_priority": "HIGH",
21
+ "original_index": 3
22
+ },
23
+ {
24
+ "subject": "UI 개선 제안",
25
+ "body": "사용자 인터페이스를 개선하는 제안입니다.",
26
+ "predicted_score": 0.7987,
27
+ "predicted_priority": "MED",
28
+ "original_index": 4
29
+ },
30
+ {
31
+ "subject": "README 오타 수정",
32
+ "body": "README.md 파일의 오타를 수정했습니다.",
33
+ "predicted_score": 0.7145,
34
+ "predicted_priority": "LOW",
35
+ "original_index": 1
36
+ }
37
+ ]
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:95ce274cc4c3ad4dbf4ee009108be55a63fd4b58b9868421cd4a589076a4ec95
3
+ size 541314292
postprocess/README.md ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Priority 후처리 규칙
2
+
3
+ 모델의 원시 점수에 키워드 기반 규칙을 적용하여 우선순위를 조정하는 후처리 시스템입니다.
4
+
5
+ ## 개요
6
+
7
+ 이 모델은 회귀 모델로 점수를 예측하지만, 실제 사용 시에는 다음과 같은 후처리 규칙을 적용하는 것을 권장합니다:
8
+
9
+ 1. **키워드 기반 규칙 적용**: 특정 키워드에 따라 우선순위를 강제 조정
10
+ 2. **배치 내 상대 정규화**: 여러 이슈를 함께 비교할 때 배치 내에서 정규화
11
+ 3. **상대적 분류**: 배치 내 상위/하위 퍼센타일 기준으로 HIGH/MED/LOW 분류
12
+
13
+ ## 규칙 종류
14
+
15
+ ### 1. LOW 강제 키워드
16
+ `low_forced_keywords`에 포함된 키워드가 있으면 무조건 LOW 우선순위로 분류됩니다.
17
+
18
+ 예시:
19
+ - "README 오타 수정" → LOW
20
+ - "문서 업데이트" → LOW
21
+ - "typo fix" → LOW
22
+
23
+ ### 2. 최소 MED 보장 키워드
24
+ `min_med_keywords`에 포함된 키워드가 있으면 최소한 MED 이상의 우선순위를 보장합니다.
25
+
26
+ 예시:
27
+ - "로그인 에러 발생" → 최소 MED
28
+ - "서버 다운 문제" → 최소 MED
29
+ - "결제 오류" → 최소 MED
30
+
31
+ ### 3. HIGH 부스트 키워드
32
+ `high_boost_keywords`에 포함된 키워드가 있으면 HIGH 우선순위로 부스트됩니다.
33
+
34
+ 예시:
35
+ - "데이터 손실 발생" → HIGH
36
+ - "무한 루프 재발" → HIGH
37
+ - "critical security issue" → HIGH
38
+
39
+ ## 사용법
40
+
41
+ ### Python 예제
42
+
43
+ ```python
44
+ import yaml
45
+ import json
46
+
47
+ # 규칙 로드
48
+ with open("postprocess/priority_rules.yaml", "r", encoding="utf-8") as f:
49
+ rules = yaml.safe_load(f)
50
+
51
+ # 이슈 텍스트
52
+ issue_text = "로그인 에러 발생, 사용자 접근 불가"
53
+
54
+ # 키워드 체크
55
+ text_lower = issue_text.lower()
56
+
57
+ # LOW 강제 체크
58
+ if any(kw in text_lower for kw in rules["low_forced_keywords"]):
59
+ priority = "LOW"
60
+ elif any(kw in text_lower for kw in rules["high_boost_keywords"]):
61
+ priority = "HIGH"
62
+ elif any(kw in text_lower for kw in rules["min_med_keywords"]):
63
+ # 모델 점수가 낮아도 최소 MED 보장
64
+ priority = max(model_priority, "MED")
65
+ else:
66
+ priority = model_priority # 모델 예측 그대로 사용
67
+ ```
68
+
69
+ ### 배치 처리 예제
70
+
71
+ ```python
72
+ import numpy as np
73
+ from scipy.stats import rankdata
74
+
75
+ def apply_postprocessing(issues, scores, rules):
76
+ """
77
+ 배치 내에서 후처리 규칙 적용
78
+ """
79
+ # 1. 키워드 기반 규칙 적용
80
+ adjusted_scores = apply_keyword_rules(issues, scores, rules)
81
+
82
+ # 2. 정규화 (quantile)
83
+ if rules["normalize_method"] == "quantile":
84
+ normalized_scores = rankdata(adjusted_scores, method='average') / len(adjusted_scores)
85
+ else:
86
+ normalized_scores = adjusted_scores
87
+
88
+ # 3. 상대적 분류
89
+ q_high = np.percentile(normalized_scores, rules["high_percentile"] * 100)
90
+ q_low = np.percentile(normalized_scores, rules["low_percentile"] * 100)
91
+
92
+ priorities = []
93
+ for score in normalized_scores:
94
+ if score >= q_high:
95
+ priorities.append("HIGH")
96
+ elif score <= q_low:
97
+ priorities.append("LOW")
98
+ else:
99
+ priorities.append("MED")
100
+
101
+ return priorities, normalized_scores
102
+ ```
103
+
104
+ ## 규칙 커스터마이징
105
+
106
+ `priority_rules.yaml` 파일을 수정하여 프로젝트에 맞는 키워드를 추가/제거할 수 있습니다.
107
+
108
+ 예시:
109
+ ```yaml
110
+ # 프로젝트 특화 키워드 추가
111
+ min_med_keywords:
112
+ - 우리회사특화키워드
113
+ - critical-path
114
+ - production-issue
115
+ ```
116
+
117
+ ## 주의사항
118
+
119
+ - 키워드 매칭은 대소문자를 구분하지 않습니다 (소문자로 변환 후 비교)
120
+ - LOW 강제 키워드가 최우선으로 적용됩니다
121
+ - HIGH 부스트 키워드가 있으면 자동으로 최소 MED도 보장됩니다
122
+ - 배치 내 정규화는 여러 이슈를 함께 비교할 때만 의미가 있습니다
postprocess/priority_rules.yaml ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Priority 후처리 규칙
2
+ # 모델 점수에 키워드 기반 규칙을 적용하여 우선순위를 조정합니다.
3
+
4
+ # LOW 우선순위 강제 키워드
5
+ # 이 키워드가 포함된 이슈는 무조건 LOW로 분류됩니다.
6
+ low_forced_keywords:
7
+ - readme
8
+ - typo
9
+ - 문서
10
+ - docs
11
+ - 오타
12
+ - 주석
13
+ - comment
14
+ - documentation
15
+ - doc
16
+
17
+ # 최소 MED 우선순위 보장 키워드
18
+ # 이 키워드가 포함된 이슈는 최소한 MED 이상의 우선순위를 가집니다.
19
+ min_med_keywords:
20
+ - 장애
21
+ - 에러
22
+ - error
23
+ - 500
24
+ - 서버
25
+ - 다운
26
+ - down
27
+ - 결제
28
+ - 주문
29
+ - order
30
+ - payment
31
+ - 로그인
32
+ - login
33
+ - auth
34
+ - jwt
35
+ - token
36
+ - 보안
37
+ - security
38
+ - 인증
39
+ - 서비스
40
+ - crash
41
+ - exception
42
+ - bug
43
+
44
+ # HIGH 우선순위 부스트 키워드
45
+ # 이 키워드가 포함된 이슈는 HIGH 우선순위로 부스트됩니다.
46
+ high_boost_keywords:
47
+ - 무한
48
+ - 재발
49
+ - 데이터 손실
50
+ - 결제 실패
51
+ - payment fail
52
+ - infinite
53
+ - recurring
54
+ - data loss
55
+ - critical
56
+ - 중복
57
+ - critical bug
58
+ - security breach
59
+ - data breach
60
+
61
+ # 점수 정규화 방법
62
+ # "quantile": 순위 기반 정규화 (권장)
63
+ # "standardize": Z-score 표준화
64
+ # "none": 정규화 없음
65
+ normalize_method: "quantile"
66
+
67
+ # 배치 내 상대 분류 기준
68
+ # 배치 내에서 상위/하위 몇 %를 HIGH/LOW로 분류할지 결정
69
+ high_percentile: 0.70 # 상위 30%를 HIGH
70
+ low_percentile: 0.30 # 하위 30%를 LOW
postprocess/to_priority.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Score를 Priority 클래스로 변환하는 함수
3
+
4
+ 사용법:
5
+ from postprocess.to_priority import to_priority
6
+
7
+ # 기본 사용 (후처리 규칙 없음)
8
+ priority = to_priority(score=0.82, text="로그인 에러")
9
+
10
+ # 후처리 규칙 포함
11
+ priority = to_priority(score=0.82, text="로그인 에러", use_rules=True)
12
+ """
13
+
14
+ import json
15
+ import os
16
+ from typing import Optional
17
+ import yaml
18
+
19
+
20
+ def to_priority(
21
+ score: float,
22
+ text: str = "",
23
+ thresholds_path: str = "score_thresholds.json",
24
+ rules_path: Optional[str] = None,
25
+ use_rules: bool = False
26
+ ) -> str:
27
+ """
28
+ 점수를 HIGH/MED/LOW 우선순위로 변환
29
+
30
+ Args:
31
+ score: 모델이 예측한 점수 (원래 스케일)
32
+ text: 이슈/커밋 텍스트 (후처리 규칙 사용 시 필요)
33
+ thresholds_path: score_thresholds.json 파일 경로
34
+ rules_path: priority_rules.yaml 파일 경로 (None이면 자동 탐색)
35
+ use_rules: 키워드 기반 후처리 규칙 사용 여부
36
+
37
+ Returns:
38
+ "HIGH", "MED", 또는 "LOW"
39
+ """
40
+ # Threshold 로드
41
+ if os.path.exists(thresholds_path):
42
+ with open(thresholds_path, "r", encoding="utf-8") as f:
43
+ thresholds = json.load(f)
44
+ else:
45
+ raise FileNotFoundError(f"Threshold 파일을 찾을 수 없습니다: {thresholds_path}")
46
+
47
+ q_low = thresholds.get("q_low", 0.0)
48
+ q_high = thresholds.get("q_high", 0.0)
49
+
50
+ # 후처리 규칙 적용 (옵션)
51
+ if use_rules:
52
+ if rules_path is None:
53
+ # 자동 탐색: postprocess/priority_rules.yaml
54
+ rules_path = os.path.join(os.path.dirname(__file__), "priority_rules.yaml")
55
+
56
+ if os.path.exists(rules_path):
57
+ with open(rules_path, "r", encoding="utf-8") as f:
58
+ rules = yaml.safe_load(f)
59
+
60
+ text_lower = text.lower()
61
+
62
+ # 1. LOW 강제 키워드 체크 (최우선)
63
+ low_keywords = rules.get("low_forced_keywords", [])
64
+ if any(kw in text_lower for kw in low_keywords):
65
+ return "LOW"
66
+
67
+ # 2. HIGH 부스트 키워드 체크
68
+ high_keywords = rules.get("high_boost_keywords", [])
69
+ if any(kw in text_lower for kw in high_keywords):
70
+ return "HIGH"
71
+
72
+ # 3. 최소 MED 보장 키워드 체크
73
+ min_med_keywords = rules.get("min_med_keywords", [])
74
+ if any(kw in text_lower for kw in min_med_keywords):
75
+ # 점수가 낮아도 최소 MED 보장
76
+ if score <= q_low:
77
+ return "MED"
78
+
79
+ # 기본 변환 (threshold 기반)
80
+ if score >= q_high:
81
+ return "HIGH"
82
+ elif score <= q_low:
83
+ return "LOW"
84
+ else:
85
+ return "MED"
86
+
87
+
88
+ def to_priority_batch(
89
+ scores: list,
90
+ texts: list = None,
91
+ thresholds_path: str = "score_thresholds.json",
92
+ rules_path: Optional[str] = None,
93
+ use_rules: bool = False
94
+ ) -> list:
95
+ """
96
+ 배치로 점수를 우선순위로 변환
97
+
98
+ Args:
99
+ scores: 점수 리스트
100
+ texts: 텍스트 리스트 (후처리 규칙 사용 시 필요)
101
+ thresholds_path: score_thresholds.json 파일 경로
102
+ rules_path: priority_rules.yaml 파일 경로
103
+ use_rules: 키워드 기반 후처리 규칙 사용 여부
104
+
105
+ Returns:
106
+ 우선순위 리스트 ["HIGH", "MED", "LOW", ...]
107
+ """
108
+ if texts is None:
109
+ texts = [""] * len(scores)
110
+
111
+ priorities = []
112
+ for score, text in zip(scores, texts):
113
+ priority = to_priority(
114
+ score=score,
115
+ text=text,
116
+ thresholds_path=thresholds_path,
117
+ rules_path=rules_path,
118
+ use_rules=use_rules
119
+ )
120
+ priorities.append(priority)
121
+
122
+ return priorities
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # 모델 사용에 필요한 최소 패키지
2
+ torch>=2.0.0
3
+ transformers>=4.30.0
4
+ numpy>=1.24.0
5
+
6
+ # 후처리 규칙 사용 시 (선택)
7
+ pyyaml>=6.0 # to_priority() 함수에서 use_rules=True 사용 시 필요
8
+ scipy>=1.10.0 # 배치 내 상대 정렬 사용 시 필요
score_thresholds.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "q_low": 0.7942798350796555,
3
+ "q_high": 0.8171828107321624,
4
+ "train_min": 0.7139821999000782,
5
+ "train_max": 0.8719141940275827,
6
+ "train_mean": 0.8040588130952866,
7
+ "train_std": 0.021295108598668053
8
+ }
special_tokens_map.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "cls_token": "[CLS]",
3
+ "mask_token": "[MASK]",
4
+ "pad_token": "[PAD]",
5
+ "sep_token": "[SEP]",
6
+ "unk_token": "[UNK]"
7
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "[PAD]",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "100": {
12
+ "content": "[UNK]",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "101": {
20
+ "content": "[CLS]",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "102": {
28
+ "content": "[SEP]",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ },
35
+ "103": {
36
+ "content": "[MASK]",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ }
43
+ },
44
+ "clean_up_tokenization_spaces": false,
45
+ "cls_token": "[CLS]",
46
+ "do_lower_case": false,
47
+ "extra_special_tokens": {},
48
+ "mask_token": "[MASK]",
49
+ "model_max_length": 512,
50
+ "pad_token": "[PAD]",
51
+ "sep_token": "[SEP]",
52
+ "strip_accents": null,
53
+ "tokenize_chinese_chars": true,
54
+ "tokenizer_class": "DistilBertTokenizer",
55
+ "unk_token": "[UNK]"
56
+ }
training_args.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:89a73c1920145b418efc2bdc3958dd1849b19730686e0ba7e5ba7af0b02b0c3a
3
+ size 5841
vocab.txt ADDED
The diff for this file is too large to render. See raw diff