# SDD: Model Group Long-Context Admission

## 위치

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
- Phase: `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`

## 상태

[승인됨]

## SDD 잠금

- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목:
  - [x] [D01] long-context 판별은 output reserve나 hidden thinking이 아니라 입력 토큰 추정치만 기준으로 한다.
  - [x] [D02] model group의 `context_window_tokens=262144`를 provider 공통 단일 요청 최대 context 계약으로 둔다.
  - [x] [D03] provider별 차이는 단일 요청 최대 context가 아니라 `long_context_capacity`로 표현한다.
  - [x] [D04] OneXPlayer dev 기준은 `ctx_size=524288`, `long_context_capacity=2`, `-np 3`이며 `ctx_size=786432`로 키우지 않는다.
  - [x] [D05] queue 앞 long 요청이 long slot을 기다릴 때 뒤의 normal 요청은 빈 일반 slot이 있으면 먼저 dispatch할 수 있다.
  - [x] [D06] long slot full은 long 요청 후보 제외 조건이며, 해당 provider의 일반 capacity가 남아 있으면 normal 요청 후보에서는 유지한다.

## 문제 / 비목표

- 문제: 현재 provider pool routing은 `in_flight`, capacity, priority 중심이라 요청이 5k인지 200k인지 알지 못한 채 provider를 선택한다. 256k 근처 요청이 1~3개 섞이는 실사용에서는 long-context 요청을 provider별 long slot으로 분산하고, long 요청이 찬 provider 또는 queue head 때문에 normal 요청 처리량이 같이 떨어지지 않게 admission 정책이 필요하다.
- 비목표:
  - output 길이나 hidden thinking token을 이용한 사전 long-context 분류
  - provider별 단일 요청 최대 context를 model group보다 낮게 허용하는 혼합 그룹
  - OneXPlayer `ctx_size`를 `786432`로 올리는 설정
  - billing/chargeback, 장기 usage ledger, 품질 기반 자동 route recommendation

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md` | Milestone 목표, 기능 Task, 완료 검토 기준 |
| Code | `apps/edge/internal/service`, `apps/edge/internal/openai`, `packages/go/config`, `configs/edge.yaml` | queue admission, OpenAI-compatible request classification, config schema 구현 기준 |
| Dev Inventory | `agent-test/dev/inventory.yaml`, `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md` | GX10, OneXPlayer, mac-mlx-vllm capacity/context smoke 기준 |
| External Provider | GX10 vLLM, OneXPlayer Lemonade, mac vLLM-MLX | provider별 runtime option은 model group context 계약과 long capacity를 만족해야 한다 |
| User Decision | D01-D06 | 이번 SDD에서 모두 결정 완료 |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| accepted | Edge OpenAI-compatible request를 수신했다 | classified | request body, model alias |
| classified_normal | 추정 입력 토큰이 Edge root `long_context_threshold_tokens` 미만이다 | queued 또는 dispatched | `estimated_input_tokens`, `context_class=normal` |
| classified_long | 추정 입력 토큰이 Edge root `long_context_threshold_tokens` 이상이다 | queued 또는 dispatched | `estimated_input_tokens`, `context_class=long` |
| queued_normal | 일반 capacity가 없어 normal 요청이 대기한다 | dispatched 또는 cancelled/error | model group queue |
| queued_long | long slot 또는 일반 capacity가 없어 long 요청이 대기한다 | dispatched 또는 cancelled/error | queue reason `long_context_capacity_full` 또는 일반 capacity full |
| dispatched_normal | provider 일반 capacity가 예약되었다 | completed/error/cancelled | provider `in_flight` 증가 |
| dispatched_long | provider 일반 capacity와 long slot이 함께 예약되었다 | completed/error/cancelled | provider `in_flight`, `long_in_flight` 증가 |
| completed | provider 응답이 정상 종료되었다 | 없음 | completion event, usage |
| error | provider, queue timeout, request validation 중 하나가 실패했다 | 없음 | error event |
| cancelled | caller 또는 runtime이 취소했다 | 없음 | cancel event |

## Interface Contract

- 계약 원문: 없음
- 입력:
  - `context_window_tokens`: model group의 단일 요청 최대 context 계약. `qwen3.6:35b` dev 기준 `262144`.
  - `long_context_threshold_tokens`: Edge root 설정의 입력 기준 long-context 분류 threshold. 초기 dev 기준 `100000`.
  - `provider.capacity`: provider의 일반 동시 요청 admission capacity.
  - `provider.total_context_tokens`: provider runtime이 long-context admission 기준으로 제공하는 전체 KV/context budget. runtime별 실제 옵션은 이 값에 매핑한다.
  - `provider.long_context_capacity`: 해당 provider가 동시에 허용하는 262144 context window급 long slot 수.
  - `estimated_input_tokens`: 요청 수신 시 Edge가 messages/tool schema/metadata를 근사 계산한 입력 토큰 수.
  - `context_class`: `normal` 또는 `long`.
- 출력:
  - provider snapshot 또는 운영 log의 `long_context_capacity`, `long_in_flight`, `estimated_input_tokens`, `context_class`, queue reason.
  - long-context 요청 대기, dispatch, 완료/실패 이벤트.
- 금지:
  - 같은 model group 안에서 provider별 단일 요청 최대 context를 262144보다 낮게 두어 라우팅 운에 따라 context 계약이 달라지게 하지 않는다.
  - OneXPlayer에서 long slot 2개를 이유로 `ctx_size=786432`로 키우지 않는다. dev 기준은 `ctx_size=524288`, `-np 3`, `long_context_capacity=2`다.
  - `provider.total_context_tokens < context_window_tokens * long_context_capacity`인 설정을 정상 long admission 설정으로 받아들이지 않는다.
  - long slot full을 normal 요청 후보 제외 조건으로 사용하지 않는다. normal 요청은 일반 capacity가 남아 있으면 기존 routing policy를 따른다.
  - long queue head가 long slot을 기다린다는 이유만으로 뒤의 normal 요청까지 무조건 막지 않는다.
  - output 길이나 hidden thinking token을 long-context 사전 분류 기준으로 쓰지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `threshold-config` | Edge root config에 long threshold가 설정되어 있다 | config check와 runtime start를 수행한다 | `long_context_threshold_tokens=100000`이 로드되고 잘못된 값은 검증에서 거부된다 |
| S02 | `model-window` | `qwen3.6:35b` model group이 provider pool에 연결되어 있다 | `/v1/chat/completions` 또는 status/catalog를 확인한다 | model group 단일 context window가 `262144`로 일관되게 기록된다 |
| S03 | `input-estimator` | 작은 설명형 요청과 100k 이상 입력 요청이 들어온다 | Edge가 요청을 queue에 넣기 전 분류한다 | 작은 요청은 normal, 100k 이상 요청은 long flag를 가진다 |
| S04 | `provider-long-capacity` | provider에 일반 capacity, total context budget, long capacity가 설정되어 있다 | config check와 long 요청 dispatch를 수행한다 | `total_context_tokens >= context_window_tokens * long_context_capacity`가 검증되고 일반 `in_flight`와 `long_in_flight`가 함께 증가/회복된다 |
| S05 | `provider-exclusion` | 특정 provider의 long slot이 이미 꽉 차 있다 | 새 long 요청을 dispatch하려 한다 | 해당 provider는 후보에서 제외되고 다른 long slot 가능 provider 또는 queue 대기를 선택한다 |
| S06 | `queue-skip` | queue 앞 long 요청이 모든 long slot full로 대기하고 뒤에 normal 요청이 있다 | normal 요청을 받을 일반 capacity가 비어 있다 | normal 요청이 head-of-line blocking 없이 먼저 dispatch되고 long slot full provider도 normal 후보로는 유지된다 |
| S07 | `dev-runtime-policy` | dev OneXPlayer provider를 설정한다 | inventory와 runtime option을 확인한다 | `ctx_size=524288`, `long_context_capacity=2`, `-np 3`이 유지되고 `786432`로 변경되지 않는다 |
| S08 | `status-logs` | normal/long/mixed 요청이 실행된다 | Control Plane status 또는 Edge log를 확인한다 | `estimated_input_tokens`, `context_class`, `long_in_flight`, queue reason으로 admission 판단을 재구성할 수 있다 |
| S09 | `capacity-smoke` | GX10, OneXPlayer, mac-mlx-vllm이 dev provider pool로 연결되어 있다 | normal 10-way, mixed long/normal, all-long-slot-full smoke를 수행한다 | 완료 후 일반/long in-flight와 queue가 모두 0으로 회복된다 |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | 완료 Evidence 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | config loader/unit test, config check output | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `threshold-config`와 S01 충족 근거 |
| S02 | model catalog/config test 또는 status snapshot | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `model-window`와 S02 충족 근거 |
| S03 | estimator unit test와 request classification log | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `input-estimator`와 S03 충족 근거 |
| S04 | config validation, scheduler/service test, provider snapshot | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `provider-long-capacity`와 S04 충족 근거 |
| S05 | scheduler/service test 또는 dev mixed smoke | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `provider-exclusion`와 S05 충족 근거 |
| S06 | queue ordering test와 dev smoke | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `queue-skip`와 S06 충족 근거 |
| S07 | `agent-test/dev/inventory.yaml`, OneX runtime load/status evidence | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `dev-runtime-policy`와 S07 충족 근거 |
| S08 | Control Plane status 또는 Edge log sample | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `status-logs`와 S08 충족 근거 |
| S09 | dev smoke script/output and final recovery snapshot | `agent-task/m-model-group-long-context-admission/...` | `Roadmap Completion`에 `capacity-smoke`와 S09 충족 근거 |

## Cross-repo Dependencies

- 없음

## Drift Check

- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- [x] Evidence Map이 code-review/complete.log에서 검증 가능하다.
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- [x] 사용자 리뷰가 필요한 항목은 `USER_REVIEW.md`에만 남겼다.

## 사용자 리뷰 이력

- 2026-07-04: 사용자가 long-context 판별은 입력 기준, model group context window는 262144, provider 차이는 `long_context_capacity`, OneXPlayer는 `ctx_size=524288`/`-np 3`/long slot 2 유지, `786432` 확장 금지를 확정했다. long slot full은 long 요청 후보 제외 조건이며 normal 요청은 일반 capacity가 남으면 기존 policy로 dispatch한다.

## 작업 컨텍스트

- 표준선: 기존 provider pool routing의 일반 admission은 `in_flight`, capacity, priority 기준을 유지하고, long-context 요청에만 추가 long slot gate를 얹는다.
- 표준선: `long_context_capacity`는 provider 전체 KV/context budget이 model group context window 몇 개분인지 나타내는 운영 설정이다.
- 표준선: provider runtime ctx/KV budget은 `total_context_tokens >= context_window_tokens * long_context_capacity` 기준으로 검증한다.
- 표준선: OneXPlayer `-np`는 이 Milestone에서 단순 최대 동시 요청 수로 취급한다.
- 후속 SDD: 요청 실행 로그와 Usage Ledger 기반
