Skip to content

Commit be93c4d

Browse files
ironAiken2agatha197
authored andcommitted
fix(FR-2467): disable session commit button until session is Running (#6452)
Resolves #6443 (FR-2467) - Disable commit button when session status is not RUNNING - Previously only checked isActive() which allowed non-running states like PREPARING, PENDING, PULLING to enable the button - [ ] Verify commit button is disabled when session is in PREPARING state - [ ] Verify commit button is disabled when session is in PENDING state - [ ] Verify commit button is enabled when session is in RUNNING state - [ ] Verify commit button remains disabled for non-owners regardless of session status 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 31b64ab commit be93c4d

4 files changed

Lines changed: 603 additions & 461 deletions

File tree

.specs/draft-auto-scaling-rule-ux/spec.md

Lines changed: 152 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,70 @@
1818

1919
`EndpointDetailPage`의 Auto Scaling Rules 테이블에서 조건을 항상 `<` 방향으로 표시한다.
2020

21+
**Strawberry API (>=26.4.0)**: `comparator` 필드 없이 `minThreshold`/`maxThreshold`로 조건을 표현한다.
22+
23+
| 저장된 상태 | 표시 형태 |
24+
|---|---|
25+
| `maxThreshold`만 설정 (상한) | `[metric_name] < [maxThreshold]` |
26+
| `minThreshold`만 설정 (하한) | `[minThreshold] < [metric_name]` |
27+
| 둘 다 설정 (범위) | `[minThreshold] < [metric_name] < [maxThreshold]` |
28+
29+
**Graphene API (<26.4.0)**: `comparator` + 단일 `threshold`로 조건을 표현한다.
30+
2131
| 저장된 comparator | 표시 형태 |
2232
|---|---|
2333
| LESS_THAN | `[metric_name] < [threshold]` |
2434
| GREATER_THAN | `[threshold] < [metric_name]` (반전) |
25-
| 범위 (minThreshold + maxThreshold) | `[minThreshold] < [metric_name] < [maxThreshold]` |
2635

27-
에디터 모달의 비교 연산자 드롭다운은 `<` (LESS_THAN)과 `>` (GREATER_THAN) 두 가지만 표시한다.
36+
에디터 모달의 비교 연산자 드롭다운은 **Legacy 단일 모드에서만** 표시하며, `<` (LESS_THAN)과 `>` (GREATER_THAN) 두 가지만 제공한다. 스키마에는 `LESS_THAN_OR_EQUAL`, `GREATER_THAN_OR_EQUAL`도 존재하지만, UI에서는 사용하지 않는다.
2837

2938
#### 2. 조건 모드 선택 (단일/범위)
3039

3140
Auto Scaling Rule 에디터 모달에 Ant Design `Segmented` 컴포넌트를 추가하여 "단일" / "범위" 모드를 전환한다.
3241

33-
- **단일 모드** (기본): 비교 연산자(`<`, `>`) 하나와 threshold 값을 입력
34-
- **범위 모드**: `[하한값]` ~ `[상한값]` 범위를 지정 (`minThreshold`/`maxThreshold` 사용)
42+
- **단일 모드** (기본): "상한" 또는 "하한" 방향을 선택하고 threshold 값을 입력
43+
- 상한 (`metric < threshold`): `maxThreshold`만 설정, `minThreshold`는 null
44+
- 하한 (`threshold < metric`): `minThreshold`만 설정, `maxThreshold`는 null
45+
- **범위 모드**: `[하한값]` ~ `[상한값]` 범위를 지정 (`minThreshold` + `maxThreshold` 동시 사용)
3546
- 하한값 >= 상한값이면 validation 에러를 표시
3647

48+
> **참고**: Strawberry API (>=26.4.0) 전용. Legacy (<26.4.0)에서는 Graphene `comparator` + 단일 `threshold`를 그대로 사용하며, 범위 모드는 지원하지 않는다.
49+
3750
#### 3. Prometheus Preset 기반 메트릭 선택
3851

3952
- `baiClient.supports('prometheus-auto-scaling-rule')` (또는 >=26.4.0 대응 키)로 기능 게이팅
40-
- **>=26.4.0**: 새로운 metric source가 추가될 예정. Condition 영역에서 `prometheusQueryPresets` 기반 Select로 metric name을 선택할 수 있도록 함. Preset 선택 시 `queryTemplate``timeWindow`가 자동 적용
41-
- **<26.4.0**: 기존 방식 유지 (metric source 선택 + metric name 수동 입력)
42-
- **TODO(needs-backend)**: 새로운 metric source의 구체적인 enum 값과 동작은 백엔드 PR 머지 후 확정
53+
- **>=26.4.0**: `AutoScalingMetricSource.PROMETHEUS`가 추가됨. Condition 영역에서 metric source로 `PROMETHEUS`를 선택하면, `prometheusQueryPresets` 기반 Select로 preset을 선택할 수 있도록 함. Preset 선택 시 `queryTemplate``timeWindow`가 자동 적용되고, `prometheusQueryPresetId`가 Rule에 저장됨
54+
- **<26.4.0**: 기존 방식 유지 (metric source `KERNEL`/`INFERENCE_FRAMEWORK` 선택 + metric name 수동 입력)
55+
56+
##### Metric Source별 UI 분기 (>=26.4.0 전용)
57+
58+
`<26.4.0`에서는 `KERNEL`/`INFERENCE_FRAMEWORK`만 표시되며, `PROMETHEUS`는 노출하지 않는다.
59+
60+
| metricSource | Preset 선택 UI | prometheusQueryPresetId | metric name |
61+
|---|---|---|---|
62+
| `KERNEL` | 숨김 | `null` | AutoComplete (자동완성 목록: `cpu_util`, `mem`, `net_rx`, `net_tx` + 자유 입력 가능) |
63+
| `INFERENCE_FRAMEWORK` | 숨김 | `null` | AutoComplete (자동완성 목록 없음, 자유 입력) |
64+
| `PROMETHEUS` | 표시 (필수) | 필수 | preset의 `metricName`에서 자동 채움 (입력 불필요) |
65+
66+
##### Preset 선택 UI 동작
67+
68+
1. metric source 드롭다운에서 `PROMETHEUS` 선택 시 Prometheus Query Preset Select가 추가로 노출
69+
2. Preset 목록은 `prometheusQueryPresets` query로 로드하여 `name`을 드롭다운에 표시
70+
3. Preset 선택 시:
71+
- `prometheusQueryPresetId`에 선택된 preset의 UUID 저장 (Relay global ID를 디코딩한 raw UUID)
72+
- `metricName`은 preset의 `metricName`에서 자동으로 채움 (사용자 입력 불필요)
73+
- `queryTemplate`을 읽기 전용으로 표시하여 사용자가 쿼리 내용 확인 가능
74+
- `timeWindow`가 자동 적용 (preset의 `timeWindow`가 null이면 사용자 입력)
75+
76+
##### Rule 목록에서 Prometheus preset 표시
77+
78+
`metricSource``PROMETHEUS`인 rule은 목록에서 preset 이름을 표시한다. `prometheusQueryPresetId``prometheusQueryPreset(id)` query를 호출하거나, preset 목록을 한 번 로드하여 클라이언트 사이드에서 매칭한다.
79+
80+
##### Rule 수정 시 주의사항
81+
82+
- `updateAutoScalingRule`은 partial update — 변경할 필드만 전달
83+
- `prometheusQueryPresetId: null`은 "미변경"(UNSET) 의미이며, 명시적으로 null로 설정할 수 없음
84+
- `metricSource``KERNEL`로 변경해도 `prometheusQueryPresetId`가 자동 클리어되지 않음 — UI에서 `KERNEL`/`INFERENCE_FRAMEWORK`일 때 preset 필드를 숨기는 것으로 처리
4385

4486
### 선택 (Nice to Have)
4587

@@ -49,52 +91,135 @@ Prometheus Preset 모드(>=26.4.0)에서 메트릭 설정 form item의 `extra`
4991

5092
- 로딩 스피너 → 결과값 텍스트 + 새로고침 아이콘 버튼
5193
- `options` 파라미터(`ExecuteQueryDefinitionOptionsInput`)로 `filterLabels`/`groupLabels` 전달
94+
- `groupLabels`는 빈 배열 `[]`이라도 반드시 전달해야 함 (생략 시 validation error)
95+
- `filterLabels``key`는 preset의 `options.filterLabels`에 정의된 것만 허용됨
96+
- `timeRange` 없이 호출하면 instant query (현재 값 1개), `timeRange` 포함 시 range query (시계열 배열)
5297

5398
## 인수 조건 (Acceptance Criteria)
5499

100+
**공통 (양쪽 모두)**
55101
- [ ] Rules 목록 테이블에서 모든 조건이 `<` 방향으로 통일되어 표시된다
56-
- [ ] GREATER_THAN으로 저장된 Rule은 threshold와 metric_name이 교환되어 표시된다
57-
- [ ] 범위 Rule(minThreshold + maxThreshold)은 `[min] < [metric] < [max]` 형태로 표시된다
58-
- [ ] 에디터 모달의 비교 연산자 드롭다운에 `<``>`만 표시된다
102+
103+
**Legacy (<26.4.0, Graphene)**
104+
- [ ] GREATER_THAN으로 저장된 Rule은 threshold와 metric_name이 교환되어 `[threshold] < [metric]`로 표시된다
105+
- [ ] 에디터 모달에서 비교 연산자 드롭다운(`<`, `>`)과 단일 threshold 입력이 동작한다
106+
- [ ] 기존 KERNEL/INFERENCE_FRAMEWORK metric source 수동 입력이 동작한다
107+
108+
**Strawberry (>=26.4.0)**
59109
- [ ] 에디터 모달에서 Segmented로 "단일"/"범위" 모드를 전환할 수 있다
110+
- [ ] 단일 모드에서 "상한"(`maxThreshold`만) 또는 "하한"(`minThreshold`만) 방향을 선택할 수 있다
111+
- [ ] 범위 모드에서 `minThreshold` + `maxThreshold`를 동시에 입력하고, `[min] < [metric] < [max]`로 표시된다
60112
- [ ] 범위 모드에서 하한값 >= 상한값이면 validation 에러가 표시된다
61-
- [ ] 범위 모드는 `minThreshold`/`maxThreshold` 필드를 사용하여 하나의 Rule로 저장된다
62-
- [ ] >=26.4.0에서 Condition 영역에 Prometheus Preset 기반 metric name Select가 표시되고, 선택 시 queryTemplate/timeWindow가 자동 반영된다
63-
- [ ] <26.4.0에서는 기존 KERNEL/INFERENCE_FRAMEWORK 수동 입력이 동작한다
113+
- [ ] metric source에 `PROMETHEUS` 옵션이 추가되고, 선택 시 Prometheus Preset Select가 표시된다
114+
- [ ] Prometheus preset 선택 시 `metricName`이 자동으로 채워지고, `queryTemplate`이 읽기 전용으로 표시된다
115+
- [ ] Prometheus preset 선택 시 `prometheusQueryPresetId` (raw UUID)가 Rule에 저장된다
116+
- [ ] `KERNEL`/`INFERENCE_FRAMEWORK` 선택 시 preset 선택 UI가 숨겨진다
117+
- [ ] Rule 목록에서 `PROMETHEUS` source인 rule은 preset 이름이 표시된다
118+
- [ ] Rule 목록은 `ModelDeployment.autoScalingRules` nested connection으로 조회된다
64119

65120
## 버전 게이팅 전략
66121

67-
| 기능 | <26.4.0 | >=26.4.0 |
122+
| 기능 | <26.4.0 (Legacy) | >=26.4.0 (Strawberry) |
68123
|---|---|---|
69-
| 비교 연산자 목록 정규화 | 적용 | 적용 |
70-
| 조건 모드 (단일/범위) | 적용 | 적용 |
71-
| Prometheus Preset 메트릭 선택 | 기존 수동 입력 유지 | Preset 기반 Select |
124+
| 비교 연산자 표시 정규화 | 적용 (LESS_THAN/GREATER_THAN 반전) | 적용 (min/maxThreshold 기반) |
125+
| 조건 모드 (단일/범위) | 단일만 지원 (Graphene에 범위 필드 없음) | 단일 + 범위 모두 지원 |
126+
| Metric Source: PROMETHEUS | 미표시 (KERNEL/INFERENCE_FRAMEWORK만) | PROMETHEUS 추가, Preset 기반 Select |
72127
| 쿼리 결과값 미리보기 | 미표시 | 표시 (Nice to Have) |
73128

129+
## 구현 전략: 파일 분리
130+
131+
Strawberry API 마이그레이션과 UX 개선을 동시에 진행하므로, 기존 Graphene 기반 컴포넌트를 legacy로 이동하고 새 파일을 정식 이름으로 생성한다.
132+
133+
### 파일 구조
134+
135+
| 파일 | API | 설명 |
136+
|---|---|---|
137+
| `AutoScalingRuleEditorModal.tsx` | Strawberry (>=26.4.0) | **신규**. 범위 모드, Prometheus preset, 정규화 포함 |
138+
| `AutoScalingRuleEditorModalLegacy.tsx` | Graphene (<26.4.0) | **기존 파일 rename**. 비교 연산자 정규화만 적용 |
139+
| `AutoScalingRuleList.tsx` | Strawberry (>=26.4.0) | **신규**. EndpointDetailPage에서 추출. 정규화된 조건 표시, 범위 표시 포함 |
140+
| `AutoScalingRuleListLegacy.tsx` | Graphene (<26.4.0) | **EndpointDetailPage에서 추출**. 비교 연산자 정규화만 적용 |
141+
142+
### 분기 방식
143+
144+
`EndpointDetailPage.tsx`에서 `baiClient.supports('prometheus-auto-scaling-rule')` (또는 대응 키)로 분기:
145+
- **>=26.4.0**: `AutoScalingRuleList` + `AutoScalingRuleEditorModal` (Strawberry query/mutation)
146+
- **<26.4.0**: `AutoScalingRuleListLegacy` + `AutoScalingRuleEditorModalLegacy` (Graphene query/mutation)
147+
148+
### Strawberry 마이그레이션 범위
149+
150+
이 spec에서 함께 처리하는 마이그레이션 항목:
151+
- Query: `endpoint_auto_scaling_rule_nodes(endpoint)``ModelDeployment.autoScalingRules` (nested connection)
152+
- Mutation: `create_endpoint_auto_scaling_rule_node``createAutoScalingRule`, `modify_endpoint_auto_scaling_rule_node``updateAutoScalingRule`, `delete_endpoint_auto_scaling_rule_node``deleteAutoScalingRule`
153+
- 필드: snake_case → camelCase, `threshold`+`comparator``minThreshold`/`maxThreshold`, `cooldown_seconds``timeWindow`, `endpoint``modelDeploymentId`
154+
74155
## 참조
75156

76157
### 현재 코드
77158

78159
| 파일 | 설명 |
79160
|---|---|
80-
| `react/src/components/AutoScalingRuleEditorModal.tsx` | Rule 추가/편집 모달. `COMPARATOR_LABELS` (line 53-58), `METRIC_NAMES_MAP` (line 60-65) |
161+
| `react/src/components/AutoScalingRuleEditorModal.tsx` | 기존 Rule 추가/편집 모달 (Graphene). rename → `AutoScalingRuleEditorModalLegacy.tsx` |
81162
| `react/src/pages/EndpointDetailPage.tsx` | Rules 목록 테이블 (line 697-926), `baiClient.supports('auto-scaling-rule')` (line 170) |
82163

83-
### GraphQL API (모든 인증된 사용자)
164+
### GraphQL API
165+
166+
#### Auto Scaling Rule (Strawberry, >=26.4.0)
167+
168+
| 쿼리/뮤테이션 | 설명 |
169+
|---|---|
170+
| `ModelDeployment.autoScalingRules(filter, orderBy, ...)` | Rule 목록 조회 — `ModelDeployment` type의 nested connection |
171+
| `createAutoScalingRule(input: CreateAutoScalingRuleInput!)` | Rule 생성 (`modelDeploymentId` 필수) |
172+
| `updateAutoScalingRule(input: UpdateAutoScalingRuleInput!)` | Rule 수정 (partial update) |
173+
| `deleteAutoScalingRule(input: DeleteAutoScalingRuleInput!)` | Rule 삭제 |
174+
175+
#### Prometheus Query Preset (Read only, 모든 인증된 사용자)
84176

85177
| 쿼리 | 설명 |
86178
|---|---|
87179
| `prometheusQueryPresets(filter, orderBy, limit, offset)` | Preset 목록 조회 |
88-
| `prometheusQueryPresetResult(id, timeRange, options, timeWindow)` | Preset 기반 쿼리 실행 결과 조회 |
180+
| `prometheusQueryPreset(id: ID!)` | Preset 단건 조회 |
181+
| `prometheusQueryPresetResult(id, timeRange, options, timeWindow)` | Preset 기반 쿼리 실행 결과 (Nice to Have) |
182+
183+
#### ID 변환 주의사항
184+
185+
- Preset 목록 조회 (`prometheusQueryPresets`) 응답의 `id`는 Relay global ID (base64 인코딩)
186+
- Rule 생성/수정 시 `prometheusQueryPresetId`에는 `toLocalId(globalId)`로 디코딩한 raw UUID를 전달
187+
- Rule 조회 응답의 `prometheusQueryPresetId`는 raw UUID로 반환됨
188+
- Preset 목록의 Relay global ID에서 `toLocalId()`로 변환한 값과 매칭하여 preset 정보를 표시
189+
190+
### 주요 타입 (스키마 기준)
191+
192+
#### AutoScalingMetricComparator (enum)
193+
`LESS_THAN`, `LESS_THAN_OR_EQUAL`, `GREATER_THAN`, `GREATER_THAN_OR_EQUAL`
194+
195+
#### AutoScalingMetricSource (enum)
196+
`KERNEL`, `INFERENCE_FRAMEWORK`, `PROMETHEUS` (PROMETHEUS는 >=26.4.0)
197+
198+
#### AutoScalingRule (type, added 25.19.0)
199+
`id`, `metricSource`, `metricName`, `minThreshold: Decimal`, `maxThreshold: Decimal`, `stepSize`, `timeWindow`, `minReplicas`, `maxReplicas`, `prometheusQueryPresetId: ID` (added 26.4.1), `createdAt`, `lastTriggeredAt`
200+
201+
#### CreateAutoScalingRuleInput
202+
`modelDeploymentId: ID!`, `metricSource: AutoScalingMetricSource!`, `metricName: String!`, `minThreshold: Decimal`, `maxThreshold: Decimal`, `stepSize: Int!`, `timeWindow: Int!`, `minReplicas: Int`, `maxReplicas: Int`, `prometheusQueryPresetId: ID`
203+
204+
#### UpdateAutoScalingRuleInput
205+
`id: ID!`, `metricSource`, `metricName`, `minThreshold`, `maxThreshold`, `stepSize`, `timeWindow`, `minReplicas`, `maxReplicas`, `prometheusQueryPresetId: ID`
206+
207+
#### QueryDefinition (type, added 26.3.0)
208+
`id`, `name`, `metricName`, `queryTemplate`, `timeWindow: String`, `options: QueryDefinitionOptions!` (`filterLabels: [String!]!`, `groupLabels: [String!]!`), `createdAt`, `updatedAt`
209+
210+
#### QueryDefinitionExecuteResult (type, added 26.3.0)
211+
`status: String!`, `resultType: String!`, `result: [QueryDefinitionMetricResult!]!`
212+
- **QueryDefinitionMetricResult**: `metric: [MetricLabelEntry!]!`, `values: [QueryDefinitionMetricResultValue!]!`
213+
- **QueryDefinitionMetricResultValue**: `timestamp: Float!`, `value: String!`
89214

90-
### 주요 타입
215+
#### ExecuteQueryDefinitionOptionsInput
216+
`filterLabels: [MetricLabelEntryInput!]`, `groupLabels: [String!]`
91217

92-
- **QueryDefinition**: `id`, `name`, `metricName`, `queryTemplate`, `timeWindow`, `options`(`filterLabels`, `groupLabels`)
93-
- **ExecuteQueryDefinitionOptionsInput**: `filterLabels`(`[MetricLabelEntryInput!]`), `groupLabels`(`[String!]`)
94-
- **CreateAutoScalingRuleInput**: `metricSource`, `metricName`, `minThreshold`, `maxThreshold`, `stepSize`, `timeWindow`, `minReplicas`, `maxReplicas`
218+
#### QueryTimeRangeInput (added 26.3.0)
219+
`start: DateTime!`, `end: DateTime!`, `step: String!`
95220

96221
## 범위 외 (Out of Scope)
97222

98-
- Admin Serving 페이지 Prometheus Query Preset CRUD (별도 Spec)
99-
- Auto Scaling Rule CRUD 자체 (이미 구현됨)
100-
- `metric_source` 필드의 백엔드 변경 대응 (TODO, 백엔드 PR 머지 후 처리)
223+
- Admin Prometheus Query Preset CUD (`adminCreatePrometheusQueryPreset`, `adminModifyPrometheusQueryPreset`, `adminDeletePrometheusQueryPreset`) — 별도 Spec
224+
- Auto Scaling Rule CRUD 자체 (이미 구현됨, 이 spec에서는 Strawberry API로 마이그레이션)
225+
- Graphene legacy 코드 완전 제거 (백엔드 최소 버전 올린 후 별도 진행)

0 commit comments

Comments
 (0)