feat: model group long context admission - classify, config, and tests
This commit is contained in:
parent
568b57ff20
commit
710d48f7d7
19 changed files with 1895 additions and 7 deletions
|
|
@ -30,7 +30,8 @@ tracked config에는 public 예시와 기본 구조만 두고, 실제 endpoint/c
|
|||
## 핵심 규칙
|
||||
|
||||
- `openai.model_routes[]`는 외부 OpenAI-compatible `model` id를 내부 `adapter + target` route로 매핑하는 compatibility catalog다.
|
||||
- `models[]`는 provider pool 방향의 canonical routing key이며 `nodes[].providers[].id`를 참조한다. `default_max_tokens`, `min_max_tokens`, `default_thinking_token_budget`은 OpenAI-compatible 요청을 내부 실행으로 넘기기 전에 적용하는 모델 단위 generation policy다.
|
||||
- `long_context_threshold_tokens`는 Edge root의 입력 토큰 추정 기준 long-context 분류 threshold다. 기본값은 `100000`이며 0 이하 값은 config load에서 거부한다.
|
||||
- `models[]`는 provider pool 방향의 canonical routing key이며 `nodes[].providers[].id`를 참조한다. `context_window_tokens`는 해당 model group의 provider 공통 단일 요청 최대 context 계약이다. `default_max_tokens`, `min_max_tokens`, `default_thinking_token_budget`은 OpenAI-compatible 요청을 내부 실행으로 넘기기 전에 적용하는 모델 단위 generation policy다.
|
||||
- `nodes[].providers[]`는 Node 아래 resource/provider catalog다. `category`는 `api`, `cli`, `local_inference` resource kind를 나타낸다.
|
||||
- `nodes[].providers[].id`는 전체 Edge config 안에서 중복되면 안 된다.
|
||||
- `nodes[].providers[].adapter`는 같은 Node 안의 enabled adapter instance key를 참조해야 한다. Exact instance key를 우선하고, legacy type-name route는 같은 type의 enabled instance가 정확히 하나일 때만 허용한다. `category: cli` resource는 enabled CLI adapter가 필요하다.
|
||||
|
|
@ -42,7 +43,7 @@ tracked config에는 public 예시와 기본 구조만 두고, 실제 endpoint/c
|
|||
|
||||
## refresh 분류 기준
|
||||
|
||||
- live apply 가능: provider capacity, provider priority, provider max queue, provider queue timeout, provider `enabled` toggle, `models[]` display/provider/generation policy mapping, legacy node runtime concurrency metadata.
|
||||
- live apply 가능: Edge root `long_context_threshold_tokens`, provider capacity, provider priority, provider max queue, provider queue timeout, provider `enabled` toggle, `models[]` display/context window/provider/generation policy mapping, legacy node runtime concurrency metadata.
|
||||
- restart required: Edge identity/listen/bootstrap/logging/metrics/console/control-plane/openai/a2a listener config, node 추가/삭제, node token/alias/agent kind, adapter 설정, provider type/category/adapter/models/health/lifecycle capability, provider-first execution fields(`provider`, `endpoint`, `base_url`, `headers`, `command`, `args`, `env`, `mode`, `resume_args`, `output_format`, `context_size`, `request_timeout_ms`) 변경.
|
||||
- rejected: candidate config load/validate 실패, invalid refresh mode, apply failure.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ long slot이 찬 provider도 일반 capacity가 남아 있으면 normal 요청
|
|||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
[진행중]
|
||||
|
||||
## 승격 조건
|
||||
|
||||
|
|
@ -48,8 +48,8 @@ long slot이 찬 provider도 일반 capacity가 남아 있으면 normal 요청
|
|||
|
||||
long-context 요청을 model group과 provider의 별도 slot 기준으로 admission하는 capability를 묶는다.
|
||||
|
||||
- [ ] [threshold-config] Edge root 설정에 `long_context_threshold_tokens`를 추가하고 dev 기준 `100000`으로 설정한다. 검증: 설정 로딩과 config check에서 threshold가 반영된다.
|
||||
- [ ] [model-window] model group에 `context_window_tokens=262144` 계약을 명시하고 같은 model group provider가 provider별 단일 요청 최대 context를 낮춰 해석하지 않도록 한다. 검증: `qwen3.6:35b` catalog/config에서 context window가 단일 기준으로 조회된다.
|
||||
- [x] [threshold-config] Edge root 설정에 `long_context_threshold_tokens`를 추가하고 dev 기준 `100000`으로 설정한다. 검증: 설정 로딩과 config check에서 threshold가 반영된다.
|
||||
- [x] [model-window] model group에 `context_window_tokens=262144` 계약을 명시하고 같은 model group provider가 provider별 단일 요청 최대 context를 낮춰 해석하지 않도록 한다. 검증: `qwen3.6:35b` catalog/config에서 context window가 단일 기준으로 조회된다.
|
||||
- [ ] [provider-long-capacity] provider별 `total_context_tokens` 또는 runtime KV/context budget 기준, `long_context_capacity`, `long_in_flight` 상태를 추가하고, long 요청이 일반 capacity와 long capacity를 함께 점유하도록 한다. 검증: `total_context_tokens >= context_window_tokens * long_context_capacity` 검증과 long 요청 dispatch 중 `long_in_flight` 증가가 확인된다.
|
||||
- [ ] [input-estimator] OpenAI-compatible chat/responses 요청의 system/developer/user messages, tool schema, metadata payload를 입력 기준으로 토큰 근사 추정해 long flag를 붙인다. 검증: 100k 이상 입력은 long, 작은 설명형 요청은 normal로 분류된다.
|
||||
- [ ] [queue-skip] queue head의 long 요청이 long slot 부족으로 대기 중이어도 뒤의 normal 요청이 빈 일반 capacity로 dispatch될 수 있게 한다. 검증: long queue 대기 중 normal 요청이 head-of-line blocking 없이 완료된다.
|
||||
|
|
@ -62,7 +62,7 @@ long-context 요청을 model group과 provider의 별도 slot 기준으로 admis
|
|||
|
||||
- 상태: 없음
|
||||
- 요청일: 없음
|
||||
- 완료 근거: 계획 Milestone이며 기능 Task가 아직 충족되지 않았다.
|
||||
- 완료 근거: 진행중 Milestone이며 남은 기능 Task는 active `agent-task/m-model-group-long-context-admission/**` plan에서 처리한다.
|
||||
- 검토 항목:
|
||||
- [ ] long-context 분류가 입력 기준으로만 수행된다.
|
||||
- [ ] 같은 model group provider는 `context_window_tokens=262144` 단일 요청 계약을 공유한다.
|
||||
|
|
@ -82,6 +82,8 @@ long-context 요청을 model group과 provider의 별도 slot 기준으로 admis
|
|||
## 작업 컨텍스트
|
||||
|
||||
- 관련 경로: `apps/edge/internal/service`, `apps/edge/internal/openai`, `apps/node/internal/adapters/openai_compat`, `packages/go/config`, `configs/edge.yaml`, `agent-test/dev/inventory.yaml`, `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md`
|
||||
- 2026-07-05 직접 처리 완료: `threshold-config`, `model-window`. 근거: `packages/go/config/config.go`, `apps/edge/internal/configrefresh/classify.go`, `configs/edge.yaml`, `agent-contract/inner/edge-config-runtime-refresh.md`, 관련 Go tests 갱신. 검증: `go test ./apps/edge/... ./packages/go/config`, `go run ./apps/edge/cmd/edge config check --config configs/edge.yaml`.
|
||||
- 2026-07-05 plan 생성: 남은 long-admission 큰 작업은 `agent-task/m-model-group-long-context-admission/01_input_estimator`부터 `06+01,02,03,04,05_capacity_smoke`까지 split plan으로 분리했다.
|
||||
- 표준선(선택): model group의 `context_window_tokens`가 단일 요청 최대 context 계약을 소유하고, provider는 이를 낮춰 해석하지 않는다.
|
||||
- 표준선(선택): `long_context_capacity`는 provider가 가진 전체 KV/context budget을 model group context window 몇 개분으로 운영할지 나타내며, 일반 `capacity`와 분리한다.
|
||||
- 표준선(선택): provider runtime의 ctx/KV budget은 `total_context_tokens >= context_window_tokens * long_context_capacity` 기준으로 검증한다.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
<!-- task=m-model-group-long-context-admission/01_input_estimator plan=0 tag=INPUT_ESTIMATOR -->
|
||||
|
||||
# Code Review Reference - INPUT_ESTIMATOR
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-05
|
||||
task=m-model-group-long-context-admission/01_input_estimator, plan=0, tag=INPUT_ESTIMATOR
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `input-estimator`: OpenAI-compatible chat/responses 요청의 입력 기준 토큰 근사 추정과 long flag 부여
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. PASS이면 runtime completion event로 roadmap target이 처리되며, 직접 roadmap을 수정하지 않는다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [INPUT_ESTIMATOR-1] OpenAI 입력 추정과 분류 propagation | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] OpenAI server가 Edge root `long_context_threshold_tokens`를 보관하고 runtime config refresh 시 갱신되게 한다.
|
||||
- [ ] chat/responses 입력에서 system/developer/user messages, tool schema, metadata payload를 입력 기준으로 근사 추정하고 `estimated_input_tokens`, `context_class`를 service request와 metadata에 붙인다. 검증: 작은 요청은 normal, 100k 이상 요청은 long이다.
|
||||
- [ ] estimator와 handler propagation 테스트를 추가한다.
|
||||
- [ ] `go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./packages/go/config` 및 config check를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G06_0.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_local_G06_0.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-model-group-long-context-admission/01_input_estimator/`로 이동한다.
|
||||
- [ ] PASS이고 task group이 `m-model-group-long-context-admission`이면 완료 이벤트 메타데이터를 보고하고 roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- long-context 분류가 입력 추정치만 사용하고 output/hidden thinking 값을 보지 않는지 확인한다.
|
||||
- chat/responses 모두 typed fields와 metadata를 전달하는지 확인한다.
|
||||
- threshold refresh 후 새 요청에서 갱신값이 적용되는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
### INPUT_ESTIMATOR-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./packages/go/config
|
||||
(output)
|
||||
$ go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, user review request state, and verification output?**
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
<!-- task=m-model-group-long-context-admission/01_input_estimator plan=0 tag=INPUT_ESTIMATOR -->
|
||||
|
||||
# Plan - INPUT_ESTIMATOR
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 구현의 필수 마지막 단계다. 코드를 바꾸고 검증을 실행한 뒤 실제 변경 내용, 계획 대비 변경 사항, 검증 stdout/stderr를 review stub에 채우고 active 파일을 그대로 둔 채 review 준비 상태로 보고한다. 종결 처리, log rename, `complete.log`, archive 이동은 code-review skill 전용이다. 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 멈춘다. 구현 중 직접 사용자에게 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`/`complete.log`를 만들지 않는다.
|
||||
|
||||
## 배경
|
||||
|
||||
Milestone은 long-context admission을 입력 토큰 추정치만 기준으로 분류하도록 고정했다. 현재 OpenAI-compatible chat/responses handler는 service 요청을 만들지만 입력 크기를 추정하거나 `normal`/`long` flag를 넘기지 않는다. 이 작업은 queue admission 전에 `estimated_input_tokens`와 `context_class`를 붙이는 foundation이다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 사용자 리뷰 요청은 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에만 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 템플릿을 따른다. 직접 사용자 프롬프트는 금지되며, code-review가 요청의 타당성 검증과 실제 `USER_REVIEW.md` 작성을 소유한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `input-estimator`: OpenAI-compatible chat/responses 요청의 입력 기준 토큰 근사 추정과 long flag 부여
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-ops/rules/project/rules.md`
|
||||
- `agent-ops/rules/private/rules.md`
|
||||
- `agent-ops/rules/common/rules-roadmap.md`
|
||||
- `agent-ops/rules/domain/platform-common.md`
|
||||
- `agent-ops/rules/domain/edge.md`
|
||||
- `agent-ops/rules/domain/testing.md`
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-contract/index.md`
|
||||
- `agent-contract/inner/edge-config-runtime-refresh.md`
|
||||
- `agent-contract/outer/openai-compatible-api.md`
|
||||
- `packages/go/config/config.go`
|
||||
- `packages/go/config/config_test.go`
|
||||
- `configs/edge.yaml`
|
||||
- `apps/edge/internal/openai/server.go`
|
||||
- `apps/edge/internal/openai/chat_handler.go`
|
||||
- `apps/edge/internal/openai/responses_handler.go`
|
||||
- `apps/edge/internal/openai/types.go`
|
||||
- `apps/edge/internal/openai/routes.go`
|
||||
- `apps/edge/internal/openai/server_test.go` (metadata/SubmitRun 관련 구간 확인)
|
||||
- `apps/edge/internal/service/run_dispatch.go`
|
||||
- `apps/edge/internal/service/service_test.go`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`, 상태 `[승인됨]`.
|
||||
- 대상 Acceptance: S03 / Milestone Task `input-estimator`.
|
||||
- Evidence Map: estimator unit test와 request classification log가 필요하다.
|
||||
- 적용: 구현 체크리스트는 chat/responses 입력 추정, metadata/typed request propagation, unit/handler test, log/metadata evidence 순서로 구성한다.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`.
|
||||
- `agent-test/local/rules.md` 읽음. Edge 변경은 `agent-test/local/edge-smoke.md`의 Go unit/config check 흐름을 따른다.
|
||||
- `agent-test/local/platform-common-smoke.md`도 config 패키지 변경 가능성 때문에 확인했다.
|
||||
- 최종 검증은 fresh 실행이 필요하므로 `go test -count=1`을 사용한다. config check는 `go run ./apps/edge/cmd/edge config check --config configs/edge.yaml`을 사용한다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- chat 입력 추정: 기존 handler tests는 metadata contract는 검증하지만 `estimated_input_tokens`/`context_class`는 검증하지 않는다. 새 테스트 필요.
|
||||
- responses 입력 추정: 기존 responses metadata contract는 있으나 long 분류가 없다. 새 테스트 필요.
|
||||
- threshold live config는 선행 직접 작업에서 config tests와 configrefresh tests가 추가되었다. 이 plan에서는 OpenAI server가 해당 값을 쓰는지만 검증한다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- 새 field 후보: `edgeservice.SubmitRunRequest.EstimatedInputTokens`, `edgeservice.SubmitRunRequest.ContextClass`. call site는 `apps/edge/internal/openai/chat_handler.go`, `apps/edge/internal/openai/responses_handler.go`, service tests의 direct literals다.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy 적용 완료. 공유 task group은 `agent-task/m-model-group-long-context-admission`.
|
||||
- sibling plans:
|
||||
- `01_input_estimator`: 선행 foundation, 의존성 없음.
|
||||
- `02+01_long_capacity`: 01의 context class가 필요하다.
|
||||
- `03+02_queue_skip`: long capacity admission 후 queue scan을 바꾼다.
|
||||
- `04+01,02_status_logs`: estimator와 long slot state가 필요하다.
|
||||
- `05+02_dev_runtime_policy`: provider config field가 필요하다.
|
||||
- `06+01,02,03,04,05_capacity_smoke`: 모든 기능과 dev policy가 필요하다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- tokenizer별 정확 계산기는 범위 제외다. SDD가 "보수적 근사 추정"을 허용한다.
|
||||
- output reserve, hidden thinking, max_tokens는 long 사전 분류 기준에서 제외한다.
|
||||
- queue admission, provider long slot, status proto는 후속 sibling plan 범위다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G06`: OpenAI handler와 service request 타입에 걸친 bounded foundation이며 local unit/config check로 검증 가능하다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] OpenAI server가 Edge root `long_context_threshold_tokens`를 보관하고 runtime config refresh 시 갱신되게 한다.
|
||||
- [ ] chat/responses 입력에서 system/developer/user messages, tool schema, metadata payload를 입력 기준으로 근사 추정하고 `estimated_input_tokens`, `context_class`를 service request와 metadata에 붙인다. 검증: 작은 요청은 normal, 100k 이상 요청은 long이다.
|
||||
- [ ] estimator와 handler propagation 테스트를 추가한다.
|
||||
- [ ] `go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./packages/go/config` 및 config check를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## [INPUT_ESTIMATOR-1] OpenAI 입력 추정과 분류 propagation
|
||||
|
||||
### 문제
|
||||
|
||||
- [apps/edge/internal/openai/chat_handler.go](/config/workspace/iop/apps/edge/internal/openai/chat_handler.go:77)는 `chatRunMetadata` 후 `chatSubmitRunRequest`를 호출하지만 입력 토큰 추정값이 없다.
|
||||
- [apps/edge/internal/openai/responses_handler.go](/config/workspace/iop/apps/edge/internal/openai/responses_handler.go:96)는 `SubmitRunRequest`를 직접 만들며 threshold를 보지 않는다.
|
||||
- [apps/edge/internal/service/run_dispatch.go](/config/workspace/iop/apps/edge/internal/service/run_dispatch.go:18)의 `SubmitRunRequest`에는 admission에서 쓸 typed context 정보가 없다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `openai.Server`에 `longContextThresholdTokens int`를 추가하고 기본값 100000을 적용한다. bootstrap과 config refresh 경로에서 `EdgeConfig.LongContextThresholdTokens`를 setter로 갱신한다.
|
||||
- package-local estimator helper를 추가한다. JSON 직렬화 가능한 입력 요소(messages/input/tools/tool_choice/metadata)를 문자열 길이 기준으로 근사 계산한다. 초기 공식은 보수적으로 `ceil(len(runes)/4) + 구조 오버헤드` 형태로 둔다.
|
||||
- chat/responses handler가 `SubmitRunRequest.EstimatedInputTokens`, `SubmitRunRequest.ContextClass`를 채우고 metadata에도 문자열로 `estimated_input_tokens`, `context_class`를 넣는다.
|
||||
|
||||
예상 전후:
|
||||
|
||||
```go
|
||||
// apps/edge/internal/openai/chat_handler.go:77
|
||||
metadata := chatRunMetadata(runMeta, req, outputPolicy)
|
||||
submitReq := chatSubmitRunRequest(dispatch, req, workspace, prompt, input, metadata)
|
||||
```
|
||||
|
||||
```go
|
||||
estimate := s.estimateChatInputTokens(req, runMeta)
|
||||
contextClass := classifyContext(estimate, s.longContextThreshold())
|
||||
metadata := chatRunMetadata(runMeta, req, outputPolicy)
|
||||
metadata["estimated_input_tokens"] = strconv.Itoa(estimate)
|
||||
metadata["context_class"] = contextClass
|
||||
submitReq := chatSubmitRunRequest(dispatch, req, workspace, prompt, input, metadata)
|
||||
submitReq.EstimatedInputTokens = estimate
|
||||
submitReq.ContextClass = contextClass
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/openai/server.go`: threshold field, default, setter/snapshot.
|
||||
- [ ] `apps/edge/internal/openai/chat_handler.go`: chat estimate/class propagation.
|
||||
- [ ] `apps/edge/internal/openai/responses_handler.go`: responses estimate/class propagation.
|
||||
- [ ] `apps/edge/internal/openai/input_estimator.go` 또는 기존 적절한 helper 파일: estimator 구현.
|
||||
- [ ] `apps/edge/internal/service/run_dispatch.go`: typed fields 추가, metadata copy 기존 동작 보존.
|
||||
- [ ] `apps/edge/internal/bootstrap/runtime.go`, `apps/edge/internal/bootstrap/refresh_admin.go`: OpenAI server threshold refresh 연결.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- [ ] `apps/edge/internal/openai/input_estimator_test.go`: 작은 chat/responses 요청은 threshold 미만, 100k 이상 text payload는 long.
|
||||
- [ ] `apps/edge/internal/openai/server_test.go`: chat/responses handler가 fake service request와 metadata에 `estimated_input_tokens`, `context_class`를 전달한다.
|
||||
- [ ] `apps/edge/internal/service/service_test.go`: `BuildRunRequest`가 새 metadata를 보존하고 typed fields 추가가 기존 dispatch metadata를 깨지 않는다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service
|
||||
```
|
||||
|
||||
기대 결과: PASS. 작은 요청은 `context_class=normal`, 큰 요청은 `context_class=long` assertion이 포함되어야 한다.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/edge/internal/openai/server.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/openai/chat_handler.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/openai/responses_handler.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/openai/input_estimator.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/openai/input_estimator_test.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/openai/server_test.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/service/run_dispatch.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/service/service_test.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/bootstrap/runtime.go` | INPUT_ESTIMATOR-1 |
|
||||
| `apps/edge/internal/bootstrap/refresh_admin.go` | INPUT_ESTIMATOR-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./packages/go/config
|
||||
go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
```
|
||||
|
||||
기대 결과: 모든 명령 PASS, config check는 `OK configs/edge.yaml`.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
<!-- task=m-model-group-long-context-admission/02+01_long_capacity plan=0 tag=LONG_CAPACITY -->
|
||||
|
||||
# Code Review Reference - LONG_CAPACITY
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> Complete the checklist, fill implementation-owned sections, then stop with active files in place and report ready for review. Do not run review finalization. Do not ask the user directly during implementation.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-05
|
||||
task=m-model-group-long-context-admission/02+01_long_capacity, plan=0, tag=LONG_CAPACITY
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `provider-long-capacity`: provider별 total context budget, long capacity, long_in_flight 상태와 동시 점유
|
||||
- `provider-exclusion`: long slot이 찬 provider를 long 후보에서 제외하고 normal 후보는 유지
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다. PASS이면 완료 이벤트만 보고하고 roadmap을 직접 수정하지 않는다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [LONG_CAPACITY-1] Provider long capacity config와 validation | [ ] |
|
||||
| [LONG_CAPACITY-2] Long slot reserve/release와 provider exclusion | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] provider config에 `total_context_tokens`, `long_context_capacity`를 추가하고 `total_context_tokens >= context_window_tokens * long_context_capacity`를 config load/check에서 검증한다.
|
||||
- [ ] config refresh classifier가 provider long capacity 관련 field를 live-apply로 분류하게 한다.
|
||||
- [ ] long 요청 dispatch 시 일반 capacity와 long capacity를 함께 reserve/release하고 `long_in_flight` 상태를 유지한다. 검증: long dispatch 중 두 counter가 증가하고 완료/오류/취소 후 회복된다.
|
||||
- [ ] long slot이 full인 provider는 long 후보에서 제외하되, normal 요청 후보에서는 일반 capacity만 기준으로 유지한다. 검증: long은 다른 provider 또는 queue 대기, normal은 남은 일반 slot으로 dispatch된다.
|
||||
- [ ] config/service/configrefresh regression tests와 config check를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] 판정을 append한다.
|
||||
- [ ] active review/plan을 `code_review_local_G08_0.log`, `plan_local_G08_0.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
- [ ] PASS split 작업이면 빈 active parent 처리 여부를 확인한다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `total_context_tokens >= context_window_tokens * long_context_capacity` 검증이 model provider mapping 기준으로 동작하는지 확인한다.
|
||||
- long request가 일반 capacity와 long capacity를 함께 점유하고 terminal path에서 둘 다 회복하는지 확인한다.
|
||||
- long slot full provider가 normal 후보에서 제외되지 않는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### LONG_CAPACITY-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
|
||||
(output)
|
||||
```
|
||||
|
||||
### LONG_CAPACITY-2 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/service
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh ./apps/edge/internal/edgevalidate ./apps/edge/internal/service
|
||||
(output)
|
||||
$ go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, user review request state, and verification output?**
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
<!-- task=m-model-group-long-context-admission/02+01_long_capacity plan=0 tag=LONG_CAPACITY -->
|
||||
|
||||
# Plan - LONG_CAPACITY
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 task는 `01_input_estimator`의 `complete.log`가 생긴 뒤 시작한다. 구현 완료 전 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채워야 하며, code-review 종결 절차는 실행하지 않는다. Milestone lock 결정이 새로 필요할 때만 review stub의 `사용자 리뷰 요청`에 기록하고 직접 사용자에게 묻지 않는다. 환경/secret/dev runtime 준비 부족은 사용자 리뷰 요청이 아니라 검증 또는 follow-up 사유다.
|
||||
|
||||
## 배경
|
||||
|
||||
Milestone은 long 요청이 일반 `in_flight`와 별도 `long_in_flight`를 함께 점유하도록 요구한다. 현재 provider pool은 일반 capacity, priority, `in_flight` 중심으로 후보를 고르며 provider별 long slot과 total context budget 검증이 없다. 이 작업은 provider config schema와 service admission 상태를 추가한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 사용자 리뷰 요청은 active review stub의 `사용자 리뷰 요청` 섹션에만 기록한다. 직접 질문, 선택지 제시, input tool 호출은 금지된다. code-review가 `USER_REVIEW.md` 작성을 소유한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `provider-long-capacity`: provider별 total context budget, long capacity, long_in_flight 상태와 동시 점유
|
||||
- `provider-exclusion`: long slot이 찬 provider를 long 후보에서 제외하고 normal 후보는 유지
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-contract/index.md`
|
||||
- `agent-contract/inner/edge-config-runtime-refresh.md`
|
||||
- `packages/go/config/config.go`
|
||||
- `packages/go/config/config_test.go`
|
||||
- `apps/edge/internal/configrefresh/classify.go`
|
||||
- `apps/edge/internal/configrefresh/classify_test.go`
|
||||
- `apps/edge/internal/edgevalidate/validate.go`
|
||||
- `configs/edge.yaml`
|
||||
- `apps/edge/internal/service/run_dispatch.go`
|
||||
- `apps/edge/internal/service/model_queue.go`
|
||||
- `apps/edge/internal/service/model_queue_test.go` (provider-pool/priority/disabled provider 구간 확인)
|
||||
- `apps/edge/internal/service/service.go`
|
||||
- `apps/edge/internal/service/service_test.go`
|
||||
- `apps/edge/internal/service/service_internal_test.go`
|
||||
- `apps/edge/internal/service/status_provider.go`
|
||||
- `apps/edge/internal/service/status_provider_test.go`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`, 상태 `[승인됨]`.
|
||||
- 대상 Acceptance: S04 `provider-long-capacity`, S05 `provider-exclusion`.
|
||||
- Evidence Map: config validation, scheduler/service test, provider snapshot 또는 dev mixed smoke.
|
||||
- 적용: config schema/validation을 먼저 추가하고, service queue state의 long slot reserve/release와 candidate exclusion을 같은 review unit에 둔다.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`.
|
||||
- local edge smoke는 Go test와 config check 중심이다.
|
||||
- 최종 검증은 fresh 실행: `go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh ./apps/edge/internal/edgevalidate ./apps/edge/internal/service` 및 config check.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- provider config numeric validation: 기존 config tests에 capacity/priority 일부는 있으나 `total_context_tokens`, `long_context_capacity` 없음. 새 boundary tests 필요.
|
||||
- live refresh classification: 새 provider fields 분류 없음. 새 test 필요.
|
||||
- long slot reserve/release: 기존 queue tests는 일반 `in_flight`와 priority 중심. long-specific reserve/release/exclusion tests 필요.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- 새 field 후보: `config.NodeProviderConf.TotalContextTokens`, `LongContextCapacity`; `candidateNode.longContextCapacity`; `inflightRec.contextClass` 또는 long 여부.
|
||||
- call sites: provider validation in `packages/go/config`, candidate build in `apps/edge/internal/service/run_dispatch.go`, queue reserve/release/snapshot in `apps/edge/internal/service/model_queue.go`, config refresh classification.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- shared task group: `agent-task/m-model-group-long-context-admission`.
|
||||
- 현재 subtask `02+01_long_capacity`는 predecessor index `01`에 의존한다.
|
||||
- 작성 시점에 `agent-task/m-model-group-long-context-admission/01_input_estimator/complete.log`는 없다. 구현 시작 전 해당 active sibling 또는 archive predecessor `complete.log`가 필요하다.
|
||||
- queue head skip은 별도 ordering risk라 `03+02_queue_skip`로 분리했다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- OpenAI input estimation은 01 범위다. 이 plan은 typed `ContextClass`가 존재한다고 보고 admission에 사용한다.
|
||||
- dev inventory/runtime option 실제 조정은 05 범위다.
|
||||
- proto/status field 추가는 04 범위다. 이 plan의 snapshot 검증은 가능하면 service-local struct/state에 한정한다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G08`: config schema와 provider pool queue admission을 함께 바꾸는 넓은 변경이지만 deterministic Go tests와 config check로 검증 가능하다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `01_input_estimator` complete 필요: `agent-task/m-model-group-long-context-admission/01_input_estimator/complete.log`.
|
||||
- predecessor가 없으면 구현하지 말고 active plan 상태를 유지한다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] provider config에 `total_context_tokens`, `long_context_capacity`를 추가하고 `total_context_tokens >= context_window_tokens * long_context_capacity`를 config load/check에서 검증한다.
|
||||
- [ ] config refresh classifier가 provider long capacity 관련 field를 live-apply로 분류하게 한다.
|
||||
- [ ] long 요청 dispatch 시 일반 capacity와 long capacity를 함께 reserve/release하고 `long_in_flight` 상태를 유지한다. 검증: long dispatch 중 두 counter가 증가하고 완료/오류/취소 후 회복된다.
|
||||
- [ ] long slot이 full인 provider는 long 후보에서 제외하되, normal 요청 후보에서는 일반 capacity만 기준으로 유지한다. 검증: long은 다른 provider 또는 queue 대기, normal은 남은 일반 slot으로 dispatch된다.
|
||||
- [ ] config/service/configrefresh regression tests와 config check를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## [LONG_CAPACITY-1] Provider long capacity config와 validation
|
||||
|
||||
### 문제
|
||||
|
||||
- [packages/go/config/config.go](/config/workspace/iop/packages/go/config/config.go:420) 부근의 provider validation은 capacity/queue/priority 중심이며 `long_context_capacity`와 `total_context_tokens`가 없다.
|
||||
- [configs/edge.yaml](/config/workspace/iop/configs/edge.yaml:120)의 model group은 `context_window_tokens`를 갖지만 provider별 budget 연결이 없다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `NodeProviderConf`에 `TotalContextTokens int`, `LongContextCapacity int`를 추가한다.
|
||||
- provider field는 non-negative로 검증한다. `long_context_capacity > 0`이면 `total_context_tokens > 0`이어야 한다.
|
||||
- model catalog provider mapping을 기준으로 `context_window_tokens > 0`인 model group에서 참조한 provider마다 `total_context_tokens >= context_window_tokens * long_context_capacity`를 검증한다.
|
||||
- `configs/edge.yaml` 예시와 dev provider entry에 값을 추가한다. 실제 dev runtime option 조정은 05에서 완료한다.
|
||||
|
||||
예상 field:
|
||||
|
||||
```go
|
||||
type NodeProviderConf struct {
|
||||
Capacity int `mapstructure:"capacity" yaml:"capacity,omitempty"`
|
||||
TotalContextTokens int `mapstructure:"total_context_tokens" yaml:"total_context_tokens,omitempty"`
|
||||
LongContextCapacity int `mapstructure:"long_context_capacity" yaml:"long_context_capacity,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `packages/go/config/config.go`: schema, validation, provider lookup helper.
|
||||
- [ ] `packages/go/config/config_test.go`: valid config, negative values, insufficient total context tests.
|
||||
- [ ] `configs/edge.yaml`: config example/update.
|
||||
- [ ] `apps/edge/internal/configrefresh/classify.go`: live-apply diff paths.
|
||||
- [ ] `apps/edge/internal/configrefresh/classify_test.go`: field classification tests.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- [ ] `TestLoadEdgeProviderLongContextCapacityValidation`.
|
||||
- [ ] `TestLoadEdgeRejectsProviderLongContextBudgetBelowModelWindow`.
|
||||
- [ ] configrefresh test for `nodes["..."].providers["..."].long_context_capacity` and `total_context_tokens`.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
|
||||
```
|
||||
|
||||
## [LONG_CAPACITY-2] Long slot reserve/release와 provider exclusion
|
||||
|
||||
### 문제
|
||||
|
||||
- [apps/edge/internal/service/run_dispatch.go](/config/workspace/iop/apps/edge/internal/service/run_dispatch.go:550)는 provider candidates를 일반 capacity/priority 기준으로 만든다.
|
||||
- [apps/edge/internal/service/model_queue.go](/config/workspace/iop/apps/edge/internal/service/model_queue.go:300)는 queue group의 일반 `inflight`만 관리한다.
|
||||
- SDD D06은 long slot full이 normal 후보 제외 조건이 아니라고 고정한다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `candidateNode`에 provider long capacity와 현재 long in-flight를 담는다.
|
||||
- `SubmitRunRequest.ContextClass == "long"`일 때만 long slot gate를 적용한다.
|
||||
- long request dispatch 성공 시 일반 `inflight`와 long `inflight`를 함께 증가시키고, terminal path에서 둘 다 감소시킨다.
|
||||
- 모든 provider의 long slot이 full이면 long 요청은 queue에 남고 queue reason을 저장할 수 있는 내부 field를 둔다. queue reason의 노출은 04에서 다룬다.
|
||||
- normal request candidate filtering은 long slot state를 보지 않는다.
|
||||
|
||||
예상 gate:
|
||||
|
||||
```go
|
||||
if req.ContextClass == "long" && cand.longContextCapacity > 0 && cand.longInFlight >= cand.longContextCapacity {
|
||||
continue
|
||||
}
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/service/run_dispatch.go`: candidates에 long capacity/current state 전달.
|
||||
- [ ] `apps/edge/internal/service/model_queue.go`: long reserve/release, queue reason storage, snapshot helper.
|
||||
- [ ] `apps/edge/internal/service/model_queue_test.go`: reserve/release/exclusion/normal 유지 tests.
|
||||
- [ ] `apps/edge/internal/service/service_internal_test.go`: refresh 후 long capacity candidate 반영 test.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- [ ] long request dispatch increments normal `inflight` and `long_in_flight`.
|
||||
- [ ] terminal completion/error/cancel decrements both.
|
||||
- [ ] provider A long full이면 long request는 provider B 또는 queue로 가고, normal request는 provider A 일반 capacity가 남으면 선택 가능.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/service
|
||||
```
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `packages/go/config/config.go` | LONG_CAPACITY-1 |
|
||||
| `packages/go/config/config_test.go` | LONG_CAPACITY-1 |
|
||||
| `configs/edge.yaml` | LONG_CAPACITY-1 |
|
||||
| `apps/edge/internal/configrefresh/classify.go` | LONG_CAPACITY-1 |
|
||||
| `apps/edge/internal/configrefresh/classify_test.go` | LONG_CAPACITY-1 |
|
||||
| `apps/edge/internal/service/run_dispatch.go` | LONG_CAPACITY-2 |
|
||||
| `apps/edge/internal/service/model_queue.go` | LONG_CAPACITY-2 |
|
||||
| `apps/edge/internal/service/model_queue_test.go` | LONG_CAPACITY-2 |
|
||||
| `apps/edge/internal/service/service_internal_test.go` | LONG_CAPACITY-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh ./apps/edge/internal/edgevalidate ./apps/edge/internal/service
|
||||
go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
```
|
||||
|
||||
기대 결과: 모든 명령 PASS, config check는 `OK configs/edge.yaml`.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<!-- task=m-model-group-long-context-admission/03+02_queue_skip plan=0 tag=QUEUE_SKIP -->
|
||||
|
||||
# Code Review Reference - QUEUE_SKIP
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is mandatory.** Complete implementation-owned sections, paste actual verification output, then stop with active files in place. Do not ask the user directly and do not run review finalization.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-05
|
||||
task=m-model-group-long-context-admission/03+02_queue_skip, plan=0, tag=QUEUE_SKIP
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `queue-skip`: long queue head가 long slot을 기다릴 때 뒤의 normal 요청이 빈 일반 capacity로 dispatch
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [QUEUE_SKIP-1] Dispatchable queue item scan | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] queue pump가 head item만 보지 않고 앞에서부터 dispatch 가능한 item을 찾아 선택하도록 바꾼다.
|
||||
- [ ] long slot 부족으로 막힌 long item은 queue에 남기고, 뒤의 normal item은 일반 capacity가 있으면 dispatch한다. 검증: long head 대기 중 normal 요청이 head-of-line blocking 없이 완료된다.
|
||||
- [ ] 같은 class/같은 dispatch 가능 조건에서는 기존 FIFO와 priority/capacity tie-breaker를 유지한다.
|
||||
- [ ] timeout, cancel, queue length accounting이 scan/pop 방식에서도 깨지지 않게 regression tests를 유지/추가한다.
|
||||
- [ ] `go test -count=1 ./apps/edge/internal/service`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] 판정을 append한다.
|
||||
- [ ] active review/plan을 `code_review_local_G07_0.log`, `plan_local_G07_0.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 archive로 이동한다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- long head blocked 상태에서 normal이 dispatch되고 long이 queue에 남는지 확인한다.
|
||||
- FIFO/priority/cancel/timeout 회귀가 없는지 확인한다.
|
||||
- normal 후보 평가에서 long slot state를 사용하지 않는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### QUEUE_SKIP-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/service
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/service
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Fill every implementation-owned section before saving.**
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<!-- task=m-model-group-long-context-admission/03+02_queue_skip plan=0 tag=QUEUE_SKIP -->
|
||||
|
||||
# Plan - QUEUE_SKIP
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 task는 `02+01_long_capacity`의 `complete.log`가 생긴 뒤 시작한다. 구현 후 active `CODE_REVIEW-*-G??.md`를 실제 변경 내용과 검증 출력으로 채우고 review 준비 상태로 보고한다. code-review 전용 종결 작업은 실행하지 않는다. Milestone lock 결정이 구현을 막을 때만 review stub의 `사용자 리뷰 요청`에 기록하며 직접 사용자에게 묻지 않는다.
|
||||
|
||||
## 배경
|
||||
|
||||
SDD는 queue 앞의 long 요청이 long slot 부족으로 대기해도 뒤의 normal 요청이 일반 capacity로 dispatch될 수 있어야 한다고 확정했다. long capacity가 도입되면 기존 head-first queue pump는 head-of-line blocking을 만들 수 있다. 이 작업은 queue 내부에서 "가장 앞의 dispatch 가능한 item"을 선택하도록 바꾼다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 selected Milestone lock decision이 실구현을 막을 때만 active review stub에 기록한다. 직접 사용자 질문과 input tool 호출은 금지되며, code-review가 `USER_REVIEW.md` 작성을 소유한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `queue-skip`: long queue head가 long slot을 기다릴 때 뒤의 normal 요청이 빈 일반 capacity로 dispatch
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `apps/edge/internal/service/model_queue.go`
|
||||
- `apps/edge/internal/service/model_queue_test.go` (queue/provider pool 관련 구간 확인)
|
||||
- `apps/edge/internal/service/run_dispatch.go`
|
||||
- `apps/edge/internal/service/service_test.go`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`, 상태 `[승인됨]`.
|
||||
- 대상 Acceptance: S06 / Milestone Task `queue-skip`.
|
||||
- Evidence Map: queue ordering test와 dev smoke. 이 plan은 deterministic queue ordering unit test를 완료 근거로 삼고 dev smoke는 06에서 수행한다.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`.
|
||||
- 적용 profile: `agent-test/local/edge-smoke.md`.
|
||||
- 최종 검증은 `go test -count=1 ./apps/edge/internal/service`.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- 기존 queue tests는 FIFO/priority/capacity 회귀를 다루지만 "long head blocked, later normal dispatchable" 시나리오가 없다. 새 ordering test 필요.
|
||||
- timeout/cancel semantics는 기존 queue tests가 일부 보장한다. scan 방식 변경 후 동일 tests가 회귀 방어 역할을 한다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- helper 추출 후보: `modelQueueManager.dispatchReadyLocked`, `modelQueueGroup.findDispatchableIndex`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- shared task group: `agent-task/m-model-group-long-context-admission`.
|
||||
- 현재 subtask `03+02_queue_skip`는 predecessor index `02`에 의존한다.
|
||||
- 작성 시점에 `agent-task/m-model-group-long-context-admission/02+01_long_capacity/complete.log`는 없다. 구현 시작 전 active 또는 archived predecessor `complete.log`가 필요하다.
|
||||
- status/log와 dev smoke는 다른 검증 성격이므로 04/06으로 분리했다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- long capacity counters와 provider exclusion은 02 범위다. 이 plan은 그 결과를 이용해 queue ordering만 바꾼다.
|
||||
- dev environment smoke는 06 범위다.
|
||||
- normal 요청의 기존 priority/tie-breaker 정책은 바꾸지 않는다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G07`: concurrency/ordering logic이라 위험도는 있으나 service package unit tests로 재현 가능하다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `02+01_long_capacity` complete 필요: `agent-task/m-model-group-long-context-admission/02+01_long_capacity/complete.log`.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] queue pump가 head item만 보지 않고 앞에서부터 dispatch 가능한 item을 찾아 선택하도록 바꾼다.
|
||||
- [ ] long slot 부족으로 막힌 long item은 queue에 남기고, 뒤의 normal item은 일반 capacity가 있으면 dispatch한다. 검증: long head 대기 중 normal 요청이 head-of-line blocking 없이 완료된다.
|
||||
- [ ] 같은 class/같은 dispatch 가능 조건에서는 기존 FIFO와 priority/capacity tie-breaker를 유지한다.
|
||||
- [ ] timeout, cancel, queue length accounting이 scan/pop 방식에서도 깨지지 않게 regression tests를 유지/추가한다.
|
||||
- [ ] `go test -count=1 ./apps/edge/internal/service`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## [QUEUE_SKIP-1] Dispatchable queue item scan
|
||||
|
||||
### 문제
|
||||
|
||||
- [apps/edge/internal/service/model_queue.go](/config/workspace/iop/apps/edge/internal/service/model_queue.go:1)는 queue group 내부에서 queue item을 순서대로 관리하지만 long slot full로 head가 막힐 때 뒤 normal item을 dispatch하는 보장이 없다.
|
||||
- SDD D05/D06은 long slot full을 normal 후보 제외 조건으로 쓰지 말라고 고정한다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- queue lock 아래에서 queue slice를 앞에서부터 scan한다.
|
||||
- 각 item에 대해 현재 provider state로 candidate 가능 여부를 평가한다.
|
||||
- 첫 dispatchable item을 queue에서 제거하고 기존 dispatch path를 사용한다.
|
||||
- head long item이 `long_context_capacity_full`인 경우 뒤 normal item scan을 계속한다. 일반 capacity full처럼 normal도 못 가는 이유면 기존처럼 대기한다.
|
||||
|
||||
예상 구조:
|
||||
|
||||
```go
|
||||
idx, cand, reason := group.findDispatchableLocked(reqState)
|
||||
if idx >= 0 {
|
||||
item := group.queue[idx]
|
||||
group.queue = append(group.queue[:idx], group.queue[idx+1:]...)
|
||||
dispatch(item, cand)
|
||||
}
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/service/model_queue.go`: scan/select/pop helper.
|
||||
- [ ] `apps/edge/internal/service/model_queue_test.go`: long head blocked + normal skip dispatch test.
|
||||
- [ ] `apps/edge/internal/service/service_test.go`: SubmitRun integration path regression if model_queue_test만으로 부족할 경우 추가.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- [ ] `TestQueueSkipsLongHeadWhenNormalBehindCanDispatch`: long slot full provider state를 만들고 long item, normal item 순으로 enqueue한다. normal이 dispatch되고 long은 queue에 남아야 한다.
|
||||
- [ ] `TestQueueSkipPreservesFIFOAmongDispatchableNormalItems`: 둘 다 dispatch 가능한 normal item 순서는 유지한다.
|
||||
- [ ] 기존 timeout/cancel tests가 실패하면 scan/pop helper에서 item context와 timer ownership을 수정한다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/service
|
||||
```
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/edge/internal/service/model_queue.go` | QUEUE_SKIP-1 |
|
||||
| `apps/edge/internal/service/model_queue_test.go` | QUEUE_SKIP-1 |
|
||||
| `apps/edge/internal/service/service_test.go` | QUEUE_SKIP-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/service
|
||||
```
|
||||
|
||||
기대 결과: PASS.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<!-- task=m-model-group-long-context-admission/04+01,02_status_logs plan=0 tag=STATUS_LOGS -->
|
||||
|
||||
# Code Review Reference - STATUS_LOGS
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is mandatory.** Paste actual proto/log/test evidence before review. Do not directly edit generated proto files and do not run review finalization.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-05
|
||||
task=m-model-group-long-context-admission/04+01,02_status_logs, plan=0, tag=STATUS_LOGS
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `status-logs`: status/log에 estimated_input_tokens, context_class, long_context_capacity, long_in_flight, long queue reason 기록
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [STATUS_LOGS-1] ProviderSnapshot long fields | [ ] |
|
||||
| [STATUS_LOGS-2] Admission log and queue reason evidence | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `ProviderSnapshot`와 관련 inner contract에 long capacity/in-flight 노출 필드를 추가하고 generated Go proto를 `make proto`로 갱신한다.
|
||||
- [ ] service status snapshot이 `long_context_capacity`, `long_in_flight`, 필요한 queued/reason 정보를 채우게 한다.
|
||||
- [ ] dispatch/queue log 또는 event metadata에 `estimated_input_tokens`, `context_class`, queue reason을 남긴다. 검증: status 또는 log sample로 admission 판단을 재구성할 수 있다.
|
||||
- [ ] control-plane relay tests가 새 provider snapshot fields를 보존하는지 검증한다.
|
||||
- [ ] proto preflight, `make proto`, 관련 Go tests를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] 판정을 append한다.
|
||||
- [ ] active review/plan을 `code_review_local_G07_0.log`, `plan_local_G07_0.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 archive로 이동한다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- proto generated 파일이 `make proto` 산출물인지 확인한다.
|
||||
- status relay가 long fields를 보존하는지 확인한다.
|
||||
- admission 판단 재구성에 필요한 estimate/class/reason이 log 또는 metadata evidence로 남는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### STATUS_LOGS-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ command -v protoc
|
||||
(output)
|
||||
$ command -v protoc-gen-go
|
||||
(output)
|
||||
$ make proto
|
||||
(output)
|
||||
$ go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane
|
||||
(output)
|
||||
```
|
||||
|
||||
### STATUS_LOGS-2 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/service
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ command -v protoc
|
||||
(output)
|
||||
$ command -v protoc-gen-go
|
||||
(output)
|
||||
$ make proto
|
||||
(output)
|
||||
$ go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane ./apps/edge/internal/transport ./apps/node/internal/transport ./apps/control-plane/internal/wire
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Fill every implementation-owned section before saving.**
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<!-- task=m-model-group-long-context-admission/04+01,02_status_logs plan=0 tag=STATUS_LOGS -->
|
||||
|
||||
# Plan - STATUS_LOGS
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 task는 `01_input_estimator`와 `02+01_long_capacity`의 `complete.log`가 생긴 뒤 시작한다. 구현 완료 후 review stub의 구현 에이전트 소유 섹션과 검증 출력을 채우고 active 파일을 유지한다. `make proto`가 필요한 경우 생성물을 직접 편집하지 않는다. 사용자에게 직접 묻지 말고 Milestone lock blocker만 `사용자 리뷰 요청` 섹션에 기록한다.
|
||||
|
||||
## 배경
|
||||
|
||||
Milestone S08은 Control Plane status 또는 Edge log로 long admission 판단을 재구성할 수 있어야 한다. estimator와 long slot state가 생겨도 status/log 표면에 `estimated_input_tokens`, `context_class`, `long_context_capacity`, `long_in_flight`, queue reason이 남지 않으면 운영자가 왜 queue가 밀렸는지 알 수 없다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 selected Milestone lock decision에 한정해 active review stub에 기록한다. 환경 준비, protoc 미설치, dev provider 미가동은 검증 blocker 또는 follow-up이지 사용자 리뷰 요청이 아니다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `status-logs`: status/log에 estimated_input_tokens, context_class, long_context_capacity, long_in_flight, long queue reason 기록
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-contract/index.md`
|
||||
- `agent-contract/inner/edge-node-runtime-wire.md`
|
||||
- `agent-contract/inner/control-plane-edge-wire.md`
|
||||
- `proto/iop/runtime.proto`
|
||||
- `Makefile`
|
||||
- `apps/edge/internal/service/status_provider.go`
|
||||
- `apps/edge/internal/service/status_provider_test.go`
|
||||
- `apps/edge/internal/service/model_queue.go`
|
||||
- `apps/edge/internal/service/model_queue_test.go`
|
||||
- `apps/edge/internal/service/run_dispatch.go`
|
||||
- `apps/edge/internal/controlplane/connector.go`
|
||||
- `apps/edge/internal/controlplane/connector_test.go` (provider snapshots relay 구간 확인)
|
||||
- `apps/edge/internal/openai/chat_handler.go`
|
||||
- `apps/edge/internal/openai/responses_handler.go`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`, 상태 `[승인됨]`.
|
||||
- 대상 Acceptance: S08 / Milestone Task `status-logs`.
|
||||
- Evidence Map: Control Plane status 또는 Edge log sample.
|
||||
- 적용: provider snapshot에는 long slot 상태를 노출하고, request/admission log에는 estimate/class/queue reason을 남긴다.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`.
|
||||
- local edge smoke profile을 적용한다.
|
||||
- proto 변경은 `Makefile`의 `proto` target을 사용한다. preflight로 `command -v protoc`와 `command -v protoc-gen-go`를 실행한다.
|
||||
- 최종 검증은 proto generation 후 service/controlplane/transport/wire Go tests를 fresh 실행한다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- ProviderSnapshot에는 long capacity/in-flight field가 없다. proto/generated code/test 갱신 필요.
|
||||
- Edge log sample은 현재 tests에서 직접 검증하지 않는다. 가능하면 zap observer 또는 event metadata test를 추가하고, 불가능하면 service status snapshot test를 필수 evidence로 둔다.
|
||||
- Control Plane relay는 existing provider snapshot pass-through tests가 있으나 새 fields assertion이 없다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- proto additions: `ProviderSnapshot.long_context_capacity`, `ProviderSnapshot.long_in_flight`, 필요 시 `long_queued` 또는 `queue_reason`.
|
||||
- generated call sites: `proto/gen/iop/runtime.pb.go`는 `make proto`로 갱신한다. 직접 편집 금지.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- shared task group: `agent-task/m-model-group-long-context-admission`.
|
||||
- 현재 subtask `04+01,02_status_logs`는 predecessor indices `01`, `02`에 의존한다.
|
||||
- 작성 시점에 두 predecessor `complete.log` 모두 없다. 구현 시작 전 active 또는 archived predecessor completion이 필요하다.
|
||||
- dev smoke evidence는 06에서 수집한다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- admission algorithm 변경은 02/03 범위다. 이 plan은 이미 존재하는 estimate/class/long counters를 노출한다.
|
||||
- 외부 Control Plane API 신규 기능은 만들지 않는다. 기존 Edge status snapshot relay에 field를 추가한다.
|
||||
- client Dart proto가 소비하는 변경은 범위 밖이다. 실제 client 소비가 확인되면 별도 plan에서 `make proto-dart`를 다룬다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G07`: proto/status/log contract 변경이 있지만 변경 표면이 내부 wire와 local tests에 한정된다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `01_input_estimator` complete 필요: `agent-task/m-model-group-long-context-admission/01_input_estimator/complete.log`.
|
||||
- `02+01_long_capacity` complete 필요: `agent-task/m-model-group-long-context-admission/02+01_long_capacity/complete.log`.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `ProviderSnapshot`와 관련 inner contract에 long capacity/in-flight 노출 필드를 추가하고 generated Go proto를 `make proto`로 갱신한다.
|
||||
- [ ] service status snapshot이 `long_context_capacity`, `long_in_flight`, 필요한 queued/reason 정보를 채우게 한다.
|
||||
- [ ] dispatch/queue log 또는 event metadata에 `estimated_input_tokens`, `context_class`, queue reason을 남긴다. 검증: status 또는 log sample로 admission 판단을 재구성할 수 있다.
|
||||
- [ ] control-plane relay tests가 새 provider snapshot fields를 보존하는지 검증한다.
|
||||
- [ ] proto preflight, `make proto`, 관련 Go tests를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## [STATUS_LOGS-1] ProviderSnapshot long fields
|
||||
|
||||
### 문제
|
||||
|
||||
- [proto/iop/runtime.proto](/config/workspace/iop/proto/iop/runtime.proto:122)의 `ProviderSnapshot`은 `capacity`, `in_flight`, `queued`만 있고 long slot state가 없다.
|
||||
- [apps/edge/internal/service/status_provider.go](/config/workspace/iop/apps/edge/internal/service/status_provider.go:20)는 provider snapshots를 Control Plane relay가 그대로 사용하게 만든다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `ProviderSnapshot`에 새 int32 fields를 추가한다. 기존 field numbers 1-12 뒤를 사용한다.
|
||||
- `edge-node-runtime-wire.md`, `control-plane-edge-wire.md`에 field 의미를 갱신한다.
|
||||
- `make proto`로 generated Go를 갱신한다.
|
||||
- queue snapshot 생성 시 provider별 long capacity/current long in-flight를 채운다.
|
||||
|
||||
예상 proto:
|
||||
|
||||
```proto
|
||||
int32 long_context_capacity = 13;
|
||||
int32 long_in_flight = 14;
|
||||
int32 long_queued = 15;
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `proto/iop/runtime.proto`
|
||||
- [ ] `proto/gen/iop/runtime.pb.go` (`make proto` 산출물)
|
||||
- [ ] `agent-contract/inner/edge-node-runtime-wire.md`
|
||||
- [ ] `agent-contract/inner/control-plane-edge-wire.md`
|
||||
- [ ] `apps/edge/internal/service/status_provider.go`
|
||||
- [ ] `apps/edge/internal/service/status_provider_test.go`
|
||||
- [ ] `apps/edge/internal/controlplane/connector_test.go`
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- [ ] status provider test: configured long capacity와 active long inflight가 snapshot field로 나온다.
|
||||
- [ ] controlplane connector test: provider snapshot long fields가 relay 중 보존된다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
command -v protoc
|
||||
command -v protoc-gen-go
|
||||
make proto
|
||||
go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane
|
||||
```
|
||||
|
||||
## [STATUS_LOGS-2] Admission log and queue reason evidence
|
||||
|
||||
### 문제
|
||||
|
||||
- `estimated_input_tokens`와 `context_class`는 request metadata로 전달되더라도 queue decision reason과 함께 운영 log에 남지 않으면 S08 재구성이 어렵다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- dispatch enqueue/blocked/selected log 또는 event metadata에 `estimated_input_tokens`, `context_class`, provider id, queue reason(`long_context_capacity_full`, `capacity_full`, `dispatched`)을 남긴다.
|
||||
- logger가 없는 내부 path라면 service result dispatch metadata 또는 event bus에 넣고 log emission이 가능한 boundary에서 zap fields로 출력한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/service/model_queue.go`: queue reason state.
|
||||
- [ ] `apps/edge/internal/service/run_dispatch.go`: request estimate/class log fields.
|
||||
- [ ] `apps/edge/internal/openai/chat_handler.go`, `responses_handler.go`: metadata keys가 01과 일치하는지 확인.
|
||||
- [ ] `apps/edge/internal/service/model_queue_test.go`: reason이 long slot full일 때 남는지 확인.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- [ ] long slot full로 queue된 item의 reason이 `long_context_capacity_full`.
|
||||
- [ ] normal request dispatch log/metadata가 long slot reason 없이 `context_class=normal`을 유지.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/service
|
||||
```
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `proto/iop/runtime.proto` | STATUS_LOGS-1 |
|
||||
| `proto/gen/iop/runtime.pb.go` | STATUS_LOGS-1 |
|
||||
| `agent-contract/inner/edge-node-runtime-wire.md` | STATUS_LOGS-1 |
|
||||
| `agent-contract/inner/control-plane-edge-wire.md` | STATUS_LOGS-1 |
|
||||
| `apps/edge/internal/service/status_provider.go` | STATUS_LOGS-1 |
|
||||
| `apps/edge/internal/service/status_provider_test.go` | STATUS_LOGS-1 |
|
||||
| `apps/edge/internal/controlplane/connector_test.go` | STATUS_LOGS-1 |
|
||||
| `apps/edge/internal/service/model_queue.go` | STATUS_LOGS-2 |
|
||||
| `apps/edge/internal/service/run_dispatch.go` | STATUS_LOGS-2 |
|
||||
| `apps/edge/internal/service/model_queue_test.go` | STATUS_LOGS-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
command -v protoc
|
||||
command -v protoc-gen-go
|
||||
make proto
|
||||
go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane ./apps/edge/internal/transport ./apps/node/internal/transport ./apps/control-plane/internal/wire
|
||||
```
|
||||
|
||||
기대 결과: 모든 명령 PASS. `make proto` 후 generated Go diff가 proto field 추가와 일치해야 한다.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<!-- task=m-model-group-long-context-admission/05+02_dev_runtime_policy plan=0 tag=DEV_RUNTIME_POLICY -->
|
||||
|
||||
# Code Review Reference - DEV_RUNTIME_POLICY
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is mandatory.** Record static and live policy evidence. External host blockers are verification blockers, not user-review requests unless a selected Milestone lock decision is unresolved.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-05
|
||||
task=m-model-group-long-context-admission/05+02_dev_runtime_policy, plan=0, tag=DEV_RUNTIME_POLICY
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `dev-runtime-policy`: GX10, OneXPlayer, mac-mlx-vllm context window/long capacity/runtime KV 설정을 inventory와 config에 맞춤
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [DEV_RUNTIME_POLICY-1] Dev provider policy alignment | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `configs/edge.yaml` dev provider entries에 `context_window_tokens=262144`, provider `total_context_tokens`, `long_context_capacity`를 SDD 기준으로 맞춘다.
|
||||
- [ ] `agent-test/dev/inventory.yaml`에 GX10, OneXPlayer, mac-mlx-vllm의 runtime context/KV policy와 long capacity를 기록한다. 검증: OneXPlayer는 `ctx_size=524288`, `long_context_capacity=2`, `-np 3`이고 `786432`이 없다.
|
||||
- [ ] `agent-test/dev/edge-smoke.md`와 `agent-test/dev/node-smoke.md`의 runtime baseline이 config/inventory와 같은 값을 말하도록 갱신한다.
|
||||
- [ ] local config check와 deterministic `rg --sort path` evidence를 실행한다.
|
||||
- [ ] 가능하면 dev live status/load evidence를 review stub에 붙이고, 불가능하면 blocker와 재개 조건을 검증 결과에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] 판정을 append한다.
|
||||
- [ ] active review/plan을 `code_review_cloud_G07_0.log`, `plan_cloud_G07_0.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 archive로 이동한다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- OneXPlayer가 `ctx_size=524288`, `long_context_capacity=2`, `-np 3`을 유지하는지 확인한다.
|
||||
- `786432`이 dev policy/config에 들어오지 않았는지 확인한다.
|
||||
- GX10/mac capacity가 일반 capacity와 long capacity를 혼동하지 않는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### DEV_RUNTIME_POLICY-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
(output)
|
||||
$ rg --sort path -n "long_context_capacity|total_context_tokens|context_window_tokens|ctx_size: 524288|--spec-type none -np 3" configs/edge.yaml agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md
|
||||
(output)
|
||||
$ ! rg --sort path -n "786432" configs/edge.yaml agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ git status --short
|
||||
(output)
|
||||
$ go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
(output)
|
||||
$ rg --sort path -n "gx10-vllm|onexplayer-lemonade|mac-mlx-vllm|long_context_capacity|total_context_tokens|context_window_tokens" configs/edge.yaml agent-test/dev/inventory.yaml
|
||||
(output)
|
||||
$ rg --sort path -n "ctx_size: 524288|--spec-type none -np 3|long_context_capacity: 2" agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md configs/edge.yaml
|
||||
(output)
|
||||
$ ! rg --sort path -n "786432" configs/edge.yaml agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Fill every implementation-owned section before saving.**
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<!-- task=m-model-group-long-context-admission/05+02_dev_runtime_policy plan=0 tag=DEV_RUNTIME_POLICY -->
|
||||
|
||||
# Plan - DEV_RUNTIME_POLICY
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 task는 `02+01_long_capacity`의 `complete.log`가 생긴 뒤 시작한다. Dev runtime policy는 config/docs/inventory와 외부 runtime assumptions가 맞물리므로 검증 evidence를 review stub에 자세히 남긴다. 환경 접근 불가, remote host 미가동, secret 부족은 사용자 리뷰 요청이 아니라 검증 blocker다. selected Milestone lock decision이 없으면 직접 사용자에게 묻지 않는다.
|
||||
|
||||
## 배경
|
||||
|
||||
Milestone은 dev provider pool의 context/KV policy를 SDD 기준과 일치시켜야 한다. 특히 OneXPlayer는 `ctx_size=524288`, `long_context_capacity=2`, `-np 3`을 유지하고 `786432`로 키우지 않는 것이 잠금 해제된 결정이다. 이 작업은 config, dev inventory, smoke 문서의 runtime policy를 같은 언어로 맞춘다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막을 때만 active review stub에 기록한다. 외부 dev host 접속 실패나 runtime process 부재는 `검증 결과` 또는 follow-up plan 대상이다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `dev-runtime-policy`: GX10, OneXPlayer, mac-mlx-vllm context window/long capacity/runtime KV 설정을 inventory와 config에 맞춤
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/dev/rules.md`
|
||||
- `agent-test/dev/edge-smoke.md`
|
||||
- `agent-test/dev/node-smoke.md`
|
||||
- `agent-test/dev/platform-common-smoke.md`
|
||||
- `agent-test/dev/inventory.yaml`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `packages/go/config/config.go`
|
||||
- `configs/edge.yaml`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`, 상태 `[승인됨]`.
|
||||
- 대상 Acceptance: S07 / Milestone Task `dev-runtime-policy`.
|
||||
- Evidence Map: `agent-test/dev/inventory.yaml`, OneX runtime load/status evidence.
|
||||
- 적용: local config check와 deterministic text evidence를 필수로 하고, 실제 runtime evidence는 가능할 때 review stub에 붙인다.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- 기본 test_env는 local로 보며 `agent-test/local/rules.md`를 적용한다.
|
||||
- dev runtime policy 확인을 위해 `agent-test/dev/*` profiles와 inventory를 추가 컨텍스트로 읽었다.
|
||||
- 외부 verification preflight:
|
||||
- runner/workdir: 현재 checkout `/config/workspace/iop`.
|
||||
- branch/dirty state: 구현 시 `git status --short` 기록.
|
||||
- config path: `configs/edge.yaml`.
|
||||
- external hosts: `gx10-vllm`, `onexplayer-lemonade`, `mac-mlx-vllm`은 `agent-test/dev/inventory.yaml` 기준.
|
||||
- required setup: 외부 host 접속이 안 되면 blocker를 기록하고 local config/docs evidence는 계속 완료한다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- config validation은 02에서 추가되는 schema tests가 담당한다.
|
||||
- dev runtime policy는 static config/docs 일관성 검증과 optional live status evidence가 필요하다. 현재 자동 smoke는 normal capacity 중심이고 long capacity policy assertion이 없다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- 새 config keys는 02에서 도입된 `total_context_tokens`, `long_context_capacity`를 사용한다.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- shared task group: `agent-task/m-model-group-long-context-admission`.
|
||||
- 현재 subtask `05+02_dev_runtime_policy`는 predecessor index `02`에 의존한다.
|
||||
- 작성 시점에 `agent-task/m-model-group-long-context-admission/02+01_long_capacity/complete.log`는 없다. 구현 시작 전 active 또는 archived predecessor completion이 필요하다.
|
||||
- 실제 mixed/all-long smoke는 06으로 분리했다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- source admission logic은 01-04 범위다. 이 plan은 config/inventory/docs policy alignment에 집중한다.
|
||||
- `ctx_size=786432` 도입은 명시적 범위 제외다.
|
||||
- GX10/mac long capacity는 runtime evidence가 불충분하면 conservative `long_context_capacity=1`로 문서화한다. OneXPlayer는 SDD 결정에 따라 `2` 고정이다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `cloud-G07`: dev external runtime assumptions와 config/docs 일관성이 핵심이며, local checks만으로 실제 policy drift를 모두 잡기 어렵다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `02+01_long_capacity` complete 필요: `agent-task/m-model-group-long-context-admission/02+01_long_capacity/complete.log`.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `configs/edge.yaml` dev provider entries에 `context_window_tokens=262144`, provider `total_context_tokens`, `long_context_capacity`를 SDD 기준으로 맞춘다.
|
||||
- [ ] `agent-test/dev/inventory.yaml`에 GX10, OneXPlayer, mac-mlx-vllm의 runtime context/KV policy와 long capacity를 기록한다. 검증: OneXPlayer는 `ctx_size=524288`, `long_context_capacity=2`, `-np 3`이고 `786432`이 없다.
|
||||
- [ ] `agent-test/dev/edge-smoke.md`와 `agent-test/dev/node-smoke.md`의 runtime baseline이 config/inventory와 같은 값을 말하도록 갱신한다.
|
||||
- [ ] local config check와 deterministic `rg --sort path` evidence를 실행한다.
|
||||
- [ ] 가능하면 dev live status/load evidence를 review stub에 붙이고, 불가능하면 blocker와 재개 조건을 검증 결과에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## [DEV_RUNTIME_POLICY-1] Dev provider policy alignment
|
||||
|
||||
### 문제
|
||||
|
||||
- [agent-test/dev/inventory.yaml](/config/workspace/iop/agent-test/dev/inventory.yaml:37)는 model group context window를 기록하지만 provider별 long capacity policy는 아직 Milestone schema와 연결되어 있지 않다.
|
||||
- [agent-test/dev/node-smoke.md](/config/workspace/iop/agent-test/dev/node-smoke.md:74)는 OneXPlayer `ctx_size=524288`, `-np 3` baseline을 기록하지만 `long_context_capacity=2`와 config validation 기준을 함께 말하지 않는다.
|
||||
- [configs/edge.yaml](/config/workspace/iop/configs/edge.yaml:121)는 `context_window_tokens=262144`를 갖지만 provider long capacity fields는 02 완료 전까지 없다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- OneXPlayer provider: `total_context_tokens=524288`, `long_context_capacity=2`, runtime docs에 `ctx_size=524288`, `-np 3`, `786432 금지`를 명시한다.
|
||||
- mac-mlx-vllm provider: `max_kv_size=262144`, `max_request_tokens=262144` 기준으로 conservative `long_context_capacity=1`, `total_context_tokens=262144`.
|
||||
- GX10 provider: `max_model_len=262144` baseline 기준으로 conservative `long_context_capacity=1`, `total_context_tokens=262144` unless runtime evidence proves more.
|
||||
- docs/inventory/config의 provider ids와 served models가 일치하는지 확인한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `configs/edge.yaml`
|
||||
- [ ] `agent-test/dev/inventory.yaml`
|
||||
- [ ] `agent-test/dev/edge-smoke.md`
|
||||
- [ ] `agent-test/dev/node-smoke.md`
|
||||
- [ ] 필요 시 `agent-test/dev/platform-common-smoke.md`에 공통 long policy reference만 추가한다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- 별도 Go test는 작성하지 않는다. config schema validation은 02 tests가 담당한다.
|
||||
- 이 task는 static policy evidence commands를 review stub에 붙인다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
rg --sort path -n "long_context_capacity|total_context_tokens|context_window_tokens|ctx_size: 524288|--spec-type none -np 3" configs/edge.yaml agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md
|
||||
! rg --sort path -n "786432" configs/edge.yaml agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md
|
||||
```
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `configs/edge.yaml` | DEV_RUNTIME_POLICY-1 |
|
||||
| `agent-test/dev/inventory.yaml` | DEV_RUNTIME_POLICY-1 |
|
||||
| `agent-test/dev/edge-smoke.md` | DEV_RUNTIME_POLICY-1 |
|
||||
| `agent-test/dev/node-smoke.md` | DEV_RUNTIME_POLICY-1 |
|
||||
| `agent-test/dev/platform-common-smoke.md` | DEV_RUNTIME_POLICY-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
rg --sort path -n "gx10-vllm|onexplayer-lemonade|mac-mlx-vllm|long_context_capacity|total_context_tokens|context_window_tokens" configs/edge.yaml agent-test/dev/inventory.yaml
|
||||
rg --sort path -n "ctx_size: 524288|--spec-type none -np 3|long_context_capacity: 2" agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md configs/edge.yaml
|
||||
! rg --sort path -n "786432" configs/edge.yaml agent-test/dev/inventory.yaml agent-test/dev/edge-smoke.md agent-test/dev/node-smoke.md
|
||||
```
|
||||
|
||||
기대 결과: config check PASS, required policy values are present, `786432` search exits non-zero.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<!-- task=m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke plan=0 tag=CAPACITY_SMOKE -->
|
||||
|
||||
# Code Review Reference - CAPACITY_SMOKE
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is mandatory.** Record preflight, smoke stdout/stderr, and status snapshot paths. External environment blockers are verification blockers, not user-review requests unless a selected Milestone lock decision is unresolved.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-05
|
||||
task=m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke, plan=0, tag=CAPACITY_SMOKE
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `capacity-smoke`: dev 환경 normal 10-way, mixed long/normal, all-long-slot-full queue 시나리오와 최종 회복 근거
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [CAPACITY_SMOKE-1] Long-context admission dev smoke | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] dev smoke profile/script를 추가하거나 기존 dev edge smoke에 long-context admission scenarios를 확장한다.
|
||||
- [ ] preflight가 source state, config check, endpoint reachability, provider identities, current provider snapshots를 기록하게 한다.
|
||||
- [ ] normal 10-way scenario를 실행하고 총 `in_flight=9`, `queued>=1` 관측 후 완료 시 `in_flight=0`, `queued=0`, `long_in_flight=0` 회복을 저장한다.
|
||||
- [ ] mixed long/normal scenario를 실행해 long slot full 중 normal이 head-of-line blocking 없이 완료되는 evidence를 저장한다.
|
||||
- [ ] all-long-slot-full scenario를 실행해 long requests가 queue 대기 후 slot 회복 시 dispatch되고 최종 counters가 0으로 회복되는 evidence를 저장한다.
|
||||
- [ ] smoke stdout/stderr와 status snapshot 파일 경로를 review stub에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] 판정을 append한다.
|
||||
- [ ] active review/plan을 `code_review_cloud_G08_0.log`, `plan_cloud_G08_0.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 archive로 이동한다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- smoke가 normal 10-way, mixed, all-long-slot-full 세 시나리오를 모두 실행했는지 확인한다.
|
||||
- 최종 snapshot마다 `in_flight=0`, `queued=0`, `long_in_flight=0` 회복 근거가 있는지 확인한다.
|
||||
- 외부 환경 실패가 있으면 정확한 blocker와 follow-up 가능성이 기록되어 있는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### CAPACITY_SMOKE-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ bash -n scripts/e2e-long-context-admission-smoke.sh
|
||||
(output)
|
||||
$ bash scripts/e2e-long-context-admission-smoke.sh --preflight --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ git rev-parse HEAD
|
||||
(output)
|
||||
$ git status --short
|
||||
(output)
|
||||
$ go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
(output)
|
||||
$ bash -n scripts/e2e-long-context-admission-smoke.sh
|
||||
(output)
|
||||
$ bash scripts/e2e-long-context-admission-smoke.sh --preflight --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
(output)
|
||||
$ bash scripts/e2e-long-context-admission-smoke.sh --scenario normal-10 --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
(output)
|
||||
$ bash scripts/e2e-long-context-admission-smoke.sh --scenario mixed --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
(output)
|
||||
$ bash scripts/e2e-long-context-admission-smoke.sh --scenario all-long-slot-full --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Fill every implementation-owned section before saving.**
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
<!-- task=m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke plan=0 tag=CAPACITY_SMOKE -->
|
||||
|
||||
# Plan - CAPACITY_SMOKE
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 task는 `01_input_estimator`, `02+01_long_capacity`, `03+02_queue_skip`, `04+01,02_status_logs`, `05+02_dev_runtime_policy`가 모두 PASS 완료된 뒤 시작한다. Dev smoke는 외부 provider pool 상태에 의존하므로 preflight, 실제 command, stdout/stderr, status snapshot evidence를 review stub에 남긴다. 외부 환경 blocker는 사용자 리뷰 요청이 아니다.
|
||||
|
||||
## 배경
|
||||
|
||||
Milestone의 마지막 evidence는 GX10, OneXPlayer, mac-mlx-vllm provider pool에서 normal 10-way, mixed long/normal, all-long-slot-full smoke를 수행하고 모든 counter가 0으로 회복되는 것을 확인하는 것이다. 이 작업은 그 smoke를 자동화하거나 문서화하고 실제 dev evidence를 남긴다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 selected Milestone lock decision blocker만 active review stub에 기록한다. dev host 미접속, runtime down, port mismatch, secret 부재, smoke 실패는 검증 결과와 follow-up 대상이다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- Task ids:
|
||||
- `capacity-smoke`: dev 환경 normal 10-way, mixed long/normal, all-long-slot-full queue 시나리오와 최종 회복 근거
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
|
||||
- `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/dev/rules.md`
|
||||
- `agent-test/dev/edge-smoke.md`
|
||||
- `agent-test/dev/node-smoke.md`
|
||||
- `agent-test/dev/platform-common-smoke.md`
|
||||
- `agent-test/dev/inventory.yaml`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `configs/edge.yaml`
|
||||
- `Makefile`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`, 상태 `[승인됨]`.
|
||||
- 대상 Acceptance: S09 / Milestone Task `capacity-smoke`.
|
||||
- Evidence Map: dev smoke script/output and final recovery snapshot.
|
||||
- 적용: smoke는 scenario별 stdout과 status snapshot 파일 경로를 review stub에 붙이는 것을 완료 조건으로 둔다.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- 기본 local rules를 읽었고, dev provider pool smoke 때문에 `agent-test/dev/*`도 읽었다.
|
||||
- 테스트 환경 프리플라이트:
|
||||
- runner/workdir: `/config/workspace/iop`.
|
||||
- source sync: 구현 시 `git status --short`, `git rev-parse HEAD` 기록.
|
||||
- config: `configs/edge.yaml`.
|
||||
- provider identities: `gx10-vllm`, `onexplayer-lemonade`, `mac-mlx-vllm`.
|
||||
- expected total general capacity from current dev docs: 9 (`gx10=4`, `onexplayer=3`, `mac=2`).
|
||||
- required status: Edge OpenAI-compatible endpoint and Control Plane/status endpoint reachable as documented by dev profiles.
|
||||
- if any host/process/port is unavailable, record exact failed command and do not turn it into user review.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- local unit tests cover estimator/admission/queue/status after predecessor tasks. They do not prove provider pool behavior with live runtimes.
|
||||
- existing dev smoke documents normal capacity+1 but not mixed long/normal or all-long-slot-full. New smoke scenario script/profile needed.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- new script/profile candidates: `scripts/e2e-long-context-admission-smoke.sh`, `agent-test/dev/long-context-admission-smoke.md`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- shared task group: `agent-task/m-model-group-long-context-admission`.
|
||||
- current subtask `06+01,02,03,04,05_capacity_smoke` depends on indices `01`, `02`, `03`, `04`, `05`.
|
||||
- 작성 시점에 all predecessor `complete.log` files are missing. 구현 시작 전 active 또는 archived predecessor completions가 모두 필요하다.
|
||||
- 이 plan은 evidence recovery가 외부 환경에 묶이므로 source implementation plans와 분리했다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- admission source bug fixing은 predecessor scope다. 이 task에서 smoke 실패가 source bug를 드러내면 review 결과에 follow-up plan을 남긴다.
|
||||
- billing/usage ledger/quality routing은 범위 제외다.
|
||||
- long prompt fixture는 synthetic text로 충분하며 tokenizer-perfect fixture는 만들지 않는다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `cloud-G08`: live dev provider pool, concurrent requests, status polling, external runtime recovery evidence가 중심이다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `01_input_estimator` complete 필요.
|
||||
- `02+01_long_capacity` complete 필요.
|
||||
- `03+02_queue_skip` complete 필요.
|
||||
- `04+01,02_status_logs` complete 필요.
|
||||
- `05+02_dev_runtime_policy` complete 필요.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] dev smoke profile/script를 추가하거나 기존 dev edge smoke에 long-context admission scenarios를 확장한다.
|
||||
- [ ] preflight가 source state, config check, endpoint reachability, provider identities, current provider snapshots를 기록하게 한다.
|
||||
- [ ] normal 10-way scenario를 실행하고 총 `in_flight=9`, `queued>=1` 관측 후 완료 시 `in_flight=0`, `queued=0`, `long_in_flight=0` 회복을 저장한다.
|
||||
- [ ] mixed long/normal scenario를 실행해 long slot full 중 normal이 head-of-line blocking 없이 완료되는 evidence를 저장한다.
|
||||
- [ ] all-long-slot-full scenario를 실행해 long requests가 queue 대기 후 slot 회복 시 dispatch되고 최종 counters가 0으로 회복되는 evidence를 저장한다.
|
||||
- [ ] smoke stdout/stderr와 status snapshot 파일 경로를 review stub에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## [CAPACITY_SMOKE-1] Long-context admission dev smoke
|
||||
|
||||
### 문제
|
||||
|
||||
- [agent-test/dev/edge-smoke.md](/config/workspace/iop/agent-test/dev/edge-smoke.md:126)는 provider capacity 총합+1 smoke를 기록하지만 S09의 mixed long/normal, all-long-slot-full 회복 evidence는 없다.
|
||||
- live provider pool에서 `long_in_flight` 회복과 queue skip이 실제 status로 보이는지 확인해야 Milestone을 닫을 수 있다.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `scripts/e2e-long-context-admission-smoke.sh`를 추가한다. 옵션은 `--preflight`, `--scenario normal-10`, `--scenario mixed`, `--scenario all-long-slot-full`, `--out-dir`.
|
||||
- synthetic prompt fixtures는 script 내부에서 생성하거나 `/tmp`에 만들고 repo에 큰 fixture를 남기지 않는다.
|
||||
- status polling은 Control Plane/Edge status endpoint에서 provider snapshots를 저장한다. unavailable이면 exact command/output을 blocker로 기록한다.
|
||||
- `agent-test/dev/edge-smoke.md` 또는 새 `agent-test/dev/long-context-admission-smoke.md`에 command, expected counters, recovery criteria를 문서화한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `scripts/e2e-long-context-admission-smoke.sh`
|
||||
- [ ] `agent-test/dev/edge-smoke.md` 또는 `agent-test/dev/long-context-admission-smoke.md`
|
||||
- [ ] 필요 시 `agent-test/dev/inventory.yaml`에 smoke endpoint/status selector 보강.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- smoke script 자체는 shell syntax/preflight command로 검증한다.
|
||||
- live scenario output이 required evidence다. unit test가 아니라 dev smoke로 완료한다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
bash -n scripts/e2e-long-context-admission-smoke.sh
|
||||
bash scripts/e2e-long-context-admission-smoke.sh --preflight --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
```
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `scripts/e2e-long-context-admission-smoke.sh` | CAPACITY_SMOKE-1 |
|
||||
| `agent-test/dev/edge-smoke.md` | CAPACITY_SMOKE-1 |
|
||||
| `agent-test/dev/long-context-admission-smoke.md` | CAPACITY_SMOKE-1 |
|
||||
| `agent-test/dev/inventory.yaml` | CAPACITY_SMOKE-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
git rev-parse HEAD
|
||||
git status --short
|
||||
go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
|
||||
bash -n scripts/e2e-long-context-admission-smoke.sh
|
||||
bash scripts/e2e-long-context-admission-smoke.sh --preflight --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
bash scripts/e2e-long-context-admission-smoke.sh --scenario normal-10 --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
bash scripts/e2e-long-context-admission-smoke.sh --scenario mixed --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
bash scripts/e2e-long-context-admission-smoke.sh --scenario all-long-slot-full --config configs/edge.yaml --out-dir /tmp/iop-long-admission-smoke
|
||||
```
|
||||
|
||||
기대 결과: 모든 smoke command PASS. 각 scenario의 최종 snapshot은 `in_flight=0`, `queued=0`, `long_in_flight=0`을 보여야 한다. 출력과 snapshot 파일 경로를 review stub에 붙인다.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -232,6 +232,7 @@ func Classify(current, candidate *config.EdgeConfig) Result {
|
|||
appendDeepIfChanged(&changes, "refresh", StatusRestartRequired, current.Refresh, candidate.Refresh)
|
||||
appendDeepIfChanged(&changes, "openai", StatusRestartRequired, current.OpenAI, candidate.OpenAI)
|
||||
appendDeepIfChanged(&changes, "a2a", StatusRestartRequired, current.A2A, candidate.A2A)
|
||||
appendIfChanged(&changes, "long_context_threshold_tokens", StatusApplied, current.LongContextThresholdTokens, candidate.LongContextThresholdTokens)
|
||||
|
||||
currentNodes := buildNodeIndex(current)
|
||||
candidateNodes := buildNodeIndex(candidate)
|
||||
|
|
@ -373,6 +374,7 @@ func Classify(current, candidate *config.EdgeConfig) Result {
|
|||
continue
|
||||
}
|
||||
appendIfChanged(&changes, fmt.Sprintf("models[%q].display_name", modelID), StatusApplied, cur.DisplayName, next.DisplayName)
|
||||
appendIfChanged(&changes, fmt.Sprintf("models[%q].context_window_tokens", modelID), StatusApplied, cur.ContextWindowTokens, next.ContextWindowTokens)
|
||||
appendIfChanged(&changes, fmt.Sprintf("models[%q].default_max_tokens", modelID), StatusApplied, cur.DefaultMaxTokens, next.DefaultMaxTokens)
|
||||
appendIfChanged(&changes, fmt.Sprintf("models[%q].min_max_tokens", modelID), StatusApplied, cur.MinMaxTokens, next.MinMaxTokens)
|
||||
appendIfChanged(&changes, fmt.Sprintf("models[%q].default_thinking_token_budget", modelID), StatusApplied, cur.DefaultThinkingTokenBudget, next.DefaultThinkingTokenBudget)
|
||||
|
|
|
|||
|
|
@ -226,6 +226,67 @@ func TestRefreshInvalidYAMLRejected(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestClassifyLongContextThresholdApplied(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
currentYAML := `
|
||||
server:
|
||||
listen: "0.0.0.0:9090"
|
||||
long_context_threshold_tokens: 100000
|
||||
nodes:
|
||||
- id: "node-1"
|
||||
alias: "n1"
|
||||
token: "tok-1"
|
||||
adapters:
|
||||
cli:
|
||||
enabled: true
|
||||
`
|
||||
candidateYAML := `
|
||||
server:
|
||||
listen: "0.0.0.0:9090"
|
||||
long_context_threshold_tokens: 120000
|
||||
nodes:
|
||||
- id: "node-1"
|
||||
alias: "n1"
|
||||
token: "tok-1"
|
||||
adapters:
|
||||
cli:
|
||||
enabled: true
|
||||
`
|
||||
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
||||
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
||||
current := buildNormalizedCurrent(t, currentPath)
|
||||
|
||||
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
||||
Mode: configrefresh.ModeDryRun,
|
||||
ConfigPath: candidatePath,
|
||||
RequestID: "test-long-threshold",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Evaluate: %v", err)
|
||||
}
|
||||
if result.Status != configrefresh.StatusApplied {
|
||||
t.Fatalf("expected status=%q, got %q changes=%+v", configrefresh.StatusApplied, result.Status, result.Changes)
|
||||
}
|
||||
found := false
|
||||
for _, change := range result.Changes {
|
||||
if change.Path == "long_context_threshold_tokens" {
|
||||
found = true
|
||||
if change.Class != configrefresh.StatusApplied {
|
||||
t.Errorf("threshold class: got %q, want applied", change.Class)
|
||||
}
|
||||
if change.Previous != "100000" || change.Next != "120000" {
|
||||
t.Errorf("threshold diff: got %s→%s", change.Previous, change.Next)
|
||||
}
|
||||
}
|
||||
if change.Class == configrefresh.StatusRestartRequired {
|
||||
t.Errorf("unexpected restart_required threshold change: %s", change.Path)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("long_context_threshold_tokens change not found in %+v", result.Changes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyModelCatalogProviderMappingApplied(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
currentYAML := `
|
||||
|
|
@ -254,6 +315,7 @@ nodes:
|
|||
models:
|
||||
- id: "qwen3.6:35b"
|
||||
display_name: "Qwen"
|
||||
context_window_tokens: 131072
|
||||
default_max_tokens: 8192
|
||||
min_max_tokens: 8192
|
||||
providers:
|
||||
|
|
@ -285,6 +347,7 @@ nodes:
|
|||
models:
|
||||
- id: "qwen3.6:35b"
|
||||
display_name: "Qwen"
|
||||
context_window_tokens: 262144
|
||||
default_max_tokens: 32768
|
||||
min_max_tokens: 32768
|
||||
default_thinking_token_budget: 8192
|
||||
|
|
@ -314,6 +377,7 @@ models:
|
|||
}
|
||||
}
|
||||
for _, path := range []string{
|
||||
`models["qwen3.6:35b"].context_window_tokens`,
|
||||
`models["qwen3.6:35b"].default_max_tokens`,
|
||||
`models["qwen3.6:35b"].min_max_tokens`,
|
||||
`models["qwen3.6:35b"].default_thinking_token_budget`,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ refresh:
|
|||
enabled: false
|
||||
listen: "127.0.0.1:19093"
|
||||
|
||||
# Requests with an estimated input token count at or above this value are
|
||||
# classified as long-context for admission policy.
|
||||
long_context_threshold_tokens: 100000
|
||||
|
||||
a2a:
|
||||
enabled: false
|
||||
listen: "0.0.0.0:8081"
|
||||
|
|
@ -82,6 +86,7 @@ openai:
|
|||
# models:
|
||||
# - id: "qwen3.6:35b"
|
||||
# display_name: "Qwen 3.6 35B"
|
||||
# context_window_tokens: 262144
|
||||
# providers:
|
||||
# vllm-gpu: "nvidia/Qwen3.6-35B-A3B-NVFP4"
|
||||
# ollama-local: "qwen3.6:35b"
|
||||
|
|
@ -113,6 +118,7 @@ console:
|
|||
models:
|
||||
- id: "qwen3.6:35b"
|
||||
display_name: "Qwen 3.6 35B"
|
||||
context_window_tokens: 262144
|
||||
default_max_tokens: 32768
|
||||
min_max_tokens: 32768
|
||||
default_thinking_token_budget: 1024
|
||||
|
|
|
|||
|
|
@ -70,7 +70,10 @@ type EdgeConfig struct {
|
|||
Console EdgeConsoleConf `mapstructure:"console" yaml:"console"`
|
||||
ControlPlane EdgeControlPlaneConf `mapstructure:"control_plane" yaml:"control_plane"`
|
||||
Refresh EdgeRefreshConf `mapstructure:"refresh" yaml:"refresh,omitempty"`
|
||||
Nodes []NodeDefinition `mapstructure:"nodes" yaml:"nodes"`
|
||||
// LongContextThresholdTokens is the input-token estimate at or above which
|
||||
// a request is classified as long-context for admission policy.
|
||||
LongContextThresholdTokens int `mapstructure:"long_context_threshold_tokens" yaml:"long_context_threshold_tokens,omitempty"`
|
||||
Nodes []NodeDefinition `mapstructure:"nodes" yaml:"nodes"`
|
||||
// Models is the top-level model catalog. Each entry defines a canonical
|
||||
// routing key (ID) and its provider-pool mapping. When set it takes
|
||||
// precedence over the legacy openai.model_routes for runtime dispatch.
|
||||
|
|
@ -261,6 +264,9 @@ type ModelCatalogEntry struct {
|
|||
ID string `mapstructure:"id" yaml:"id"`
|
||||
// DisplayName is a human-readable name shown in operation dashboards.
|
||||
DisplayName string `mapstructure:"display_name" yaml:"display_name,omitempty"`
|
||||
// ContextWindowTokens is the model group's single-request maximum context
|
||||
// contract shared by every provider in the group.
|
||||
ContextWindowTokens int `mapstructure:"context_window_tokens" yaml:"context_window_tokens,omitempty"`
|
||||
// DefaultMaxTokens is injected when OpenAI-compatible requests omit an
|
||||
// output-token limit for this model group.
|
||||
DefaultMaxTokens int `mapstructure:"default_max_tokens" yaml:"default_max_tokens,omitempty"`
|
||||
|
|
@ -309,6 +315,9 @@ func (e ModelCatalogEntry) Validate(resolvedProviderIDs map[string]struct{}, ser
|
|||
if e.DefaultThinkingTokenBudget < 0 {
|
||||
return fmt.Errorf("models[%q].default_thinking_token_budget must be non-negative", id)
|
||||
}
|
||||
if e.ContextWindowTokens < 0 {
|
||||
return fmt.Errorf("models[%q].context_window_tokens must be non-negative", id)
|
||||
}
|
||||
if e.DefaultMaxTokens > 0 && e.MinMaxTokens > 0 && e.DefaultMaxTokens < e.MinMaxTokens {
|
||||
return fmt.Errorf("models[%q].default_max_tokens must be greater than or equal to min_max_tokens", id)
|
||||
}
|
||||
|
|
@ -572,6 +581,9 @@ func LoadEdge(cfgFile string) (*EdgeConfig, error) {
|
|||
if err := validateOpenAIRoutes(cfg.OpenAI.ModelRoutes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cfg.LongContextThresholdTokens <= 0 {
|
||||
return nil, fmt.Errorf("long_context_threshold_tokens must be positive")
|
||||
}
|
||||
|
||||
// Collect all provider IDs from nodes[].providers[] for cross-referencing
|
||||
// and validate uniqueness within each node.
|
||||
|
|
@ -999,4 +1011,5 @@ func setEdgeDefaults(v *viper.Viper) {
|
|||
v.SetDefault("control_plane.reconnect_interval_sec", 5)
|
||||
v.SetDefault("refresh.enabled", false)
|
||||
v.SetDefault("refresh.listen", "127.0.0.1:19093")
|
||||
v.SetDefault("long_context_threshold_tokens", 100000)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package config_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -140,6 +141,59 @@ func TestLoadEdge_OpenAIDefaults(t *testing.T) {
|
|||
if cfg.Metrics.Port != 19092 {
|
||||
t.Fatalf("expected metrics.port=19092, got %d", cfg.Metrics.Port)
|
||||
}
|
||||
if cfg.LongContextThresholdTokens != 100000 {
|
||||
t.Fatalf("expected long_context_threshold_tokens=100000 by default, got %d", cfg.LongContextThresholdTokens)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_LongContextThresholdOverride(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
yaml := `
|
||||
server:
|
||||
listen: "0.0.0.0:9090"
|
||||
long_context_threshold_tokens: 120000
|
||||
`
|
||||
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
||||
t.Fatalf("write yaml: %v", err)
|
||||
}
|
||||
cfg, err := config.LoadEdge(f)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
if cfg.LongContextThresholdTokens != 120000 {
|
||||
t.Fatalf("expected long_context_threshold_tokens=120000, got %d", cfg.LongContextThresholdTokens)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_LongContextThresholdRejectsNonPositive(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
value int
|
||||
}{
|
||||
{name: "zero", value: 0},
|
||||
{name: "negative", value: -1},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
yaml := fmt.Sprintf(`
|
||||
server:
|
||||
listen: "0.0.0.0:9090"
|
||||
long_context_threshold_tokens: %d
|
||||
`, tc.value)
|
||||
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
||||
t.Fatalf("write yaml: %v", err)
|
||||
}
|
||||
_, err := config.LoadEdge(f)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-positive long_context_threshold_tokens")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "long_context_threshold_tokens must be positive") {
|
||||
t.Fatalf("expected error mentioning positive threshold, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_OpenAIOverride(t *testing.T) {
|
||||
|
|
@ -1802,6 +1856,7 @@ server:
|
|||
models:
|
||||
- id: "qwen3.6:35b"
|
||||
display_name: "Qwen 3.6 35B"
|
||||
context_window_tokens: 262144
|
||||
default_max_tokens: 32768
|
||||
min_max_tokens: 32768
|
||||
default_thinking_token_budget: 8192
|
||||
|
|
@ -1842,6 +1897,9 @@ nodes:
|
|||
if m.DisplayName != "Qwen 3.6 35B" {
|
||||
t.Errorf("models[0].DisplayName = %q, want %q", m.DisplayName, "Qwen 3.6 35B")
|
||||
}
|
||||
if m.ContextWindowTokens != 262144 {
|
||||
t.Errorf("models[0].ContextWindowTokens = %d, want 262144", m.ContextWindowTokens)
|
||||
}
|
||||
if m.DefaultMaxTokens != 32768 {
|
||||
t.Errorf("models[0].DefaultMaxTokens = %d, want 32768", m.DefaultMaxTokens)
|
||||
}
|
||||
|
|
@ -1976,6 +2034,38 @@ nodes:
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_ModelCatalogContextWindowRejectsNegative(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
yaml := `
|
||||
server:
|
||||
listen: "0.0.0.0:9090"
|
||||
models:
|
||||
- id: "qwen3.6:35b"
|
||||
context_window_tokens: -1
|
||||
providers:
|
||||
vllm-gpu: "model-a"
|
||||
nodes:
|
||||
- id: "node-01"
|
||||
providers:
|
||||
- id: "vllm-gpu"
|
||||
type: "vllm"
|
||||
category: "api"
|
||||
models:
|
||||
- "model-a"
|
||||
`
|
||||
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
||||
t.Fatalf("write yaml: %v", err)
|
||||
}
|
||||
_, err := config.LoadEdge(f)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for negative context_window_tokens")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "context_window_tokens must be non-negative") {
|
||||
t.Fatalf("expected error mentioning context_window_tokens, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_ModelCatalogServedModelMembershipMissing(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
|
|
@ -2265,6 +2355,7 @@ func TestModelCatalogEntry_Validate(t *testing.T) {
|
|||
e := config.ModelCatalogEntry{
|
||||
ID: "qwen3.6:35b",
|
||||
DisplayName: "Qwen 3.6 35B",
|
||||
ContextWindowTokens: 262144,
|
||||
DefaultMaxTokens: 32768,
|
||||
MinMaxTokens: 32768,
|
||||
DefaultThinkingTokenBudget: 8192,
|
||||
|
|
@ -2320,6 +2411,14 @@ func TestModelCatalogEntry_Validate(t *testing.T) {
|
|||
name string
|
||||
entry config.ModelCatalogEntry
|
||||
}{
|
||||
{
|
||||
name: "negative context window tokens",
|
||||
entry: config.ModelCatalogEntry{
|
||||
ID: "test",
|
||||
ContextWindowTokens: -1,
|
||||
Providers: map[string]string{"vllm-gpu": "m"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative default max tokens",
|
||||
entry: config.ModelCatalogEntry{
|
||||
|
|
|
|||
Loading…
Reference in a new issue