update: edge node store, transport, config changes and task archive
This commit is contained in:
parent
137b3a5301
commit
8bb0c2babe
14 changed files with 1121 additions and 127 deletions
|
|
@ -0,0 +1,63 @@
|
|||
# User Review Resolved - m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke
|
||||
|
||||
## 요청 일시
|
||||
|
||||
2026-06-11
|
||||
|
||||
## 상태
|
||||
|
||||
RESOLVED
|
||||
|
||||
## 사유
|
||||
|
||||
- 유형: environment-blocked -> user-resolved
|
||||
- 현재 리뷰 회차: 3
|
||||
- 최종 판정: PASS/RESOLVED
|
||||
- 요약: repo-owned normalize correctness follow-up은 통과했고, 사용자 소유 원격 환경에서 두 Ollama inference route와 동시 실행 smoke 증거가 수집되어 사용자가 완료 처리 진행을 승인했다.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_cloud_G07_0.log` | `code_review_cloud_G07_0.log` | FAIL | legacy adapter normalize가 raw config conflict를 숨기는 repo-owned correctness issue 발견 |
|
||||
| `plan_cloud_G07_1.log` | `code_review_cloud_G07_1.log` | FAIL | same-name explicit disabled instance가 enabled legacy adapter를 가릴 수 있는 `Enabled` mismatch issue 발견 |
|
||||
| `plan_cloud_G07_2.log` | `code_review_cloud_G07_2.log` | FAIL | repo-owned normalize fixes/tests/build는 통과했으나 field smoke 원격 환경 prerequisite이 남음 |
|
||||
| `USER_REVIEW.md` | user decision | RESOLVED | 사용자 제공 원격 Node 연결 후 `gemma4_ext:26b`, `gemma4:26b` 개별/병렬 호출 성공 증거로 stop state 해소 |
|
||||
|
||||
## 해소 근거
|
||||
|
||||
- 사용자 제공 원격 Node가 edge bootstrap으로 연결되었고, Edge route table은 두 모델을 같은 configured Node `node-ollama-1`로 라우팅하도록 설정되었다.
|
||||
- `curl -fsS http://toki-labs.com:18081/healthz` 응답: `{"status":"ok"}`
|
||||
- `curl -fsS http://toki-labs.com:18081/v1/models` 응답에 `gemma4_ext:26b`, `gemma4:26b`가 노출됨.
|
||||
- `gemma4_ext:26b` 개별 호출 응답: `ext-ok`
|
||||
- `gemma4:26b` 개별 호출 응답: `base-ok`
|
||||
- 초기 병렬 호출에서 `concurrency limit exceeded (limit=1)`가 확인되어 `node-ollama-1.runtime.concurrency`를 `2`로 조정하고 Edge를 재시작함.
|
||||
- 동시성 조정 후 병렬 호출 응답:
|
||||
- `gemma4_ext:26b` -> `ext-parallel-ok`
|
||||
- `gemma4:26b` -> `base-parallel-ok`
|
||||
- 재검증 시 Edge 로그에 `concurrency limit exceeded`가 더 이상 나타나지 않음.
|
||||
- 테스트 종료 후 사용자 요청에 따라 Edge 서버를 종료했고, `http://toki-labs.com:18081/healthz` 연결 실패로 포트가 내려간 상태를 확인함.
|
||||
- 원래 Roadmap 문구의 CLI target 별도 호출은 이번 사용자 리뷰에서 사용자가 지정한 두 Ollama 추론엔진 smoke 범위로 축소 승인되었다. 완료 근거에는 실제 수집한 증거만 기록한다.
|
||||
- 현재 archive plan: `plan_cloud_G07_2.log`
|
||||
- 현재 archive review: `code_review_cloud_G07_2.log`
|
||||
- 검증 명령: `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`; `go test -count=1 ./...`; `go build ./apps/edge/... ./apps/node/...`
|
||||
- 실제 출력: 대상 테스트 PASS, 전체 Go 테스트 PASS, edge/node build 성공. 상세 stdout은 `code_review_cloud_G07_2.log`의 `검증 결과` 섹션에 보관됨.
|
||||
- 차단 해소 판단 근거: 원격 Edge 주소, 연결된 Node, 두 Ollama endpoint 준비와 접근 권한이 사용자 action으로 제공되었고, Edge 경유 OpenAI-compatible 호출이 두 model route에서 성공했다.
|
||||
|
||||
## 사용자 결정
|
||||
|
||||
- [x] 사용자 소유 원격 환경을 준비하고 두 inference route smoke 증거를 수집했다.
|
||||
- [x] 수집된 증거로 사용자 리뷰 stop state를 완료/PASS 해소하는 데 동의했다.
|
||||
- [x] 테스트 종료 후 Edge 서버를 내렸다.
|
||||
|
||||
## 재개 조건
|
||||
|
||||
해소 완료. 추가 재개 조건 없음.
|
||||
|
||||
## 다음 실행 힌트
|
||||
|
||||
없음.
|
||||
|
||||
## 종료 규칙
|
||||
|
||||
- `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<!-- task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke plan=0 tag=TEST -->
|
||||
|
||||
# Code Review Reference - TEST
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> Record the exact remote/local environment, commands, stdout/stderr, and any artifact paths.
|
||||
> If blocked by user-owned server, node, token, secret, or model endpoint preparation, fill `사용자 리뷰 요청` and stop.
|
||||
> Do not ask the user directly.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-10
|
||||
task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke, plan=0, tag=TEST
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- Task ids:
|
||||
- `field-smoke`: split-host smoke에서 하나의 연결된 Node가 최소 두 개의 inference model target과 하나의 CLI target을 같은 Edge-Node 연결로 호출할 수 있음을 확인한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.
|
||||
|
||||
smoke 증거가 같은 Edge-Node 연결과 같은 Node id를 가리키는지 확인한다. 환경 blocker가 repo-owned인지 user-owned인지 구분한다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [TEST-1] Split Host Multi Target Smoke | [사용자 리뷰 요청] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] split-host smoke 사전 조건과 실제 실행 config를 기록한다. (사전 회귀 테스트 통과, 환경 prerequisite 기록)
|
||||
- [ ] Edge `/v1/models`가 두 inference model target을 노출하는 증거를 수집한다. — 원격 환경 필요
|
||||
- [ ] 같은 연결된 Node를 통해 inference model target 두 개를 각각 호출하는 증거를 수집한다. — 원격 환경 필요
|
||||
- [ ] 같은 연결된 Node를 통해 CLI target 하나를 호출하는 증거를 수집한다. — 원격 환경 필요
|
||||
- [x] 실패 시 repo-owned fix와 user-owned prerequisite을 구분해 기록한다. (아래 참조)
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. — 원격 smoke 완료 후 채울 수 있음
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [x] `코드리뷰 결과`에 판정을 append한다.
|
||||
- [x] active `CODE_REVIEW-cloud-G07.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-cloud-G07.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 archive 이동한다.
|
||||
- [x] WARN/FAIL이면 후속 상태 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
사전 회귀 실행 중 `iop/apps/edge/internal/transport` 테스트 2개 실패 발견. smoke와 직접 관련된 repo-owned 버그였으므로 이 task 범위 내에서 최소 수정 처리.
|
||||
|
||||
수정 내용:
|
||||
1. `packages/go/config/config.go`: `NormalizeAdapters` exported 함수 추가 + `normalizeAdapters` idempotent 보장 (이미 promote된 이름은 재추가 안 함 + `hasInstanceName`/`hasVllmInstanceName` 헬퍼)
|
||||
2. `apps/edge/internal/node/store.go`: `LoadFromConfig`에서 `config.NormalizeAdapters` 호출 추가 — `LoadEdge`를 거치지 않고 직접 `AdaptersConf.Ollama`를 사용한 `NodeDefinition`이 `BuildConfigPayload`에서 adapter를 찾지 못하는 버그 수정
|
||||
3. `apps/edge/internal/transport/server_test.go`: `TestBuildConfigPayload_OllamaVllmOneof` 테스트를 normalize된 `OllamaInstances`/`VllmInstances` 사용으로 수정 (테스트가 잘못된 normalize 전 상태를 사용했던 문제)
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `normalizeAdapters`를 idempotent하게 만든 것은 `LoadEdge` → `validateEdgeConfig` → `LoadFromConfig` 이중 호출 경로에서 중복 promote를 막기 위해서다. legacy `Ollama.Enabled`를 초기화하지 않고 이름 중복 검사로 처리해 기존 동작(`LoadEdge` 이후 `Ollama.Enabled` 접근)을 유지했다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
- 상태: 차단
|
||||
- 사유 유형: 사용자 소유 외부 환경 준비 필요
|
||||
- 결정 필요: 없음 (설계 결정은 없음)
|
||||
- 차단 근거: split-host smoke의 핵심 증거(Edge `/v1/models` multi-target 노출, inference 호출 두 개, CLI target 호출)는 원격 runner(`toki-labs.com`)와 연결된 Node, Node token, 모델 endpoint(Ollama)가 준비되어야 실행 가능하다. 현재 local 작업 컨테이너에서는 접근 불가.
|
||||
- 실행한 검증/명령:
|
||||
- `go test -count=1 ./...` → 전체 PASS (사전 회귀 포함)
|
||||
- `go build ./apps/edge/... ./apps/node/...` → 빌드 성공
|
||||
- 자동 후속 불가 이유: 원격 Edge 주소(`toki-labs.com:18081`), 연결된 Node, Node token, Ollama endpoint는 사용자 소유 환경이다. 원격 runner SSH 접근도 현재 컨테이너에서 불가.
|
||||
- 재개 조건:
|
||||
1. 원격 runner(`toki-labs.com`)에서 Edge와 Node가 실행 중이고, multi-target Edge config(inference 두 개 + CLI 한 개)가 적용되어 있어야 한다.
|
||||
2. `curl -fsS http://toki-labs.com:18081/v1/models` 응답에 두 inference route가 보여야 한다.
|
||||
3. 동일 Node id로 inference 호출 두 개와 CLI 호출 한 개가 성공해야 한다.
|
||||
4. 위 증거를 `CODE_REVIEW-cloud-G07.md` 검증 결과 섹션에 채운 뒤 코드리뷰를 재실행한다.
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `/v1/models`에 두 inference route가 실제로 보인다.
|
||||
- 두 inference 호출과 CLI 호출이 같은 connected Node id/alias를 사용한다.
|
||||
- secret/token 값이 문서에 노출되지 않았다.
|
||||
- 실패가 있으면 repo-owned follow-up과 user-owned blocker가 분리되어 있다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### TEST-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./...
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.137s
|
||||
ok iop/apps/control-plane/internal/wire 1.487s
|
||||
ok iop/apps/edge/cmd/edge 0.066s
|
||||
ok iop/apps/edge/internal/bootstrap 0.024s
|
||||
ok iop/apps/edge/internal/controlplane 4.448s
|
||||
ok iop/apps/edge/internal/edgecmd 0.019s
|
||||
ok iop/apps/edge/internal/events 0.008s
|
||||
ok iop/apps/edge/internal/input 0.006s
|
||||
ok iop/apps/edge/internal/input/a2a 0.009s
|
||||
ok iop/apps/edge/internal/node 0.005s
|
||||
ok iop/apps/edge/internal/openai 1.509s
|
||||
ok iop/apps/edge/internal/opsconsole 0.007s
|
||||
ok iop/apps/edge/internal/service 0.006s
|
||||
ok iop/apps/edge/internal/transport 2.018s
|
||||
ok iop/apps/node/cmd/node 0.011s
|
||||
ok iop/apps/node/internal/adapters 0.008s
|
||||
ok iop/apps/node/internal/adapters/cli 42.117s
|
||||
ok iop/apps/node/internal/adapters/cli/status 39.872s
|
||||
ok iop/apps/node/internal/adapters/ollama 0.013s
|
||||
ok iop/apps/node/internal/adapters/vllm 0.006s
|
||||
ok iop/apps/node/internal/bootstrap 0.264s
|
||||
ok iop/apps/node/internal/node 0.118s
|
||||
ok iop/apps/node/internal/router 0.005s
|
||||
ok iop/apps/node/internal/store 0.047s
|
||||
ok iop/apps/node/internal/terminal 0.432s
|
||||
ok iop/apps/node/internal/transport 5.141s
|
||||
ok iop/packages/go/audit 0.002s
|
||||
ok iop/packages/go/config 0.018s
|
||||
ok iop/packages/go/hostsetup 0.006s
|
||||
ok iop/packages/go/observability 0.009s
|
||||
```
|
||||
|
||||
원격 smoke (healthz, /v1/models, inference 호출, CLI 호출)는 사용자 소유 환경 필요로 차단.
|
||||
|
||||
### 최종 검증
|
||||
|
||||
원격 환경 준비 후 수집 예정:
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: FAIL
|
||||
- 차원별 평가:
|
||||
- correctness: Fail
|
||||
- completeness: Fail
|
||||
- test coverage: Fail
|
||||
- API contract: Fail
|
||||
- code quality: Pass
|
||||
- plan deviation: Warn
|
||||
- verification trust: Pass
|
||||
- 발견된 문제:
|
||||
- Required: `packages/go/config/config.go:310`와 `packages/go/config/config.go:319`의 `hasInstanceName`/`hasVllmInstanceName` guard가 raw config의 첫 normalize와 이미 normalize된 config의 재호출을 구분하지 못합니다. 사용자가 legacy `ollama.enabled=true` 또는 `vllm.enabled=true`와 같은 이름의 explicit instance(`ollama`/`vllm`)를 함께 둔 경우, 새 코드는 legacy promotion을 건너뛰고 충돌을 조용히 통과시킵니다. 특히 explicit instance가 disabled이거나 endpoint/base_url이 다르면 `BuildConfigPayload`는 slice만 읽기 때문에 enabled legacy adapter가 Node payload에서 사라질 수 있습니다. idempotence는 유지하되 첫 normalize의 legacy/explicit same-name 충돌은 error로 거부하거나, 기존 instance가 promoted legacy 값과 정확히 같은 경우에만 재호출로 간주하도록 고치고, Ollama/Vllm 각각에 대해 `LoadEdge` 및 `node.LoadFromConfig` 회귀 테스트를 추가하세요.
|
||||
- 다음 단계: WARN/FAIL follow-up으로 active plan/review를 다시 작성한다. user-owned 원격 smoke blocker는 유효하지만, repo-owned normalize correctness issue가 먼저 해결되어야 하므로 이번 루프에서는 `USER_REVIEW.md`를 만들지 않는다.
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
<!-- task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke plan=1 tag=REVIEW_TEST -->
|
||||
|
||||
# Code Review Reference - REVIEW_TEST
|
||||
|
||||
> **[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 user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts 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 the needed decision in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-11
|
||||
task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke, plan=1, tag=REVIEW_TEST
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- Task ids:
|
||||
- `field-smoke`: split-host smoke에서 하나의 연결된 Node가 최소 두 개의 inference model target과 하나의 CLI target을 같은 Edge-Node 연결로 호출할 수 있음을 확인한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md` → `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` → `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [REVIEW_TEST-1] NormalizeAdapters same-name conflict and smoke unblock | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] `NormalizeAdapters`가 이미 normalize된 config를 재호출해도 중복 promote하지 않는 회귀 테스트를 Ollama/Vllm 각각 추가한다. (`TestNormalizeAdapters_OllamaIdempotent`, `TestNormalizeAdapters_VllmIdempotent`)
|
||||
- [x] legacy enabled config와 같은 이름의 explicit instance가 raw config에 함께 있을 때 disabled 또는 다른 endpoint/base_url/context_size 충돌을 조용히 허용하지 않는 회귀 테스트를 Ollama/Vllm 각각 추가한다. (`TestNormalizeAdapters_OllamaConflictErrors`, `TestNormalizeAdapters_VllmConflictErrors`)
|
||||
- [x] `NormalizeAdapters`를 수정해 exact promoted legacy instance 재호출은 idempotent하게 허용하고, 첫 normalize의 same-name conflicting explicit instance는 error로 반환하게 한다. (`normalizeAdapters`에 `findOllamaInstance`/`findVllmInstance` 기반 충돌 감지 추가)
|
||||
- [x] `node.LoadFromConfig`가 raw legacy `NodeDefinition`을 받아도 normalized copy를 저장해 `BuildConfigPayload`에 Ollama/Vllm adapter가 포함되는지 검증한다. (`TestLoadFromConfig_LegacyOllamaInPayload`, `TestLoadFromConfig_LegacyVllmInPayload`)
|
||||
- [x] `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`, `go test -count=1 ./...`, `go build ./apps/edge/... ./apps/node/...`를 실행하고 실제 stdout/stderr를 기록한다.
|
||||
- [x] 원격 field 환경이 준비되어 있으면 `/healthz`, `/v1/models`, inference target 두 개 호출, CLI target 호출이 같은 connected Node id/alias를 쓰는 증거를 수집한다. 준비되어 있지 않으면 `사용자 리뷰 요청`에 user-owned blocker와 재개 조건을 기록한다. — 원격 환경 차단, `사용자 리뷰 요청` 참조
|
||||
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/`를 `agent-task/archive/YYYY/MM/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-node-multi-target-serving-foundation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
없음. 체크리스트 전 항목을 계획대로 구현했다.
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `normalizeAdapters`의 same-name 충돌 감지 기준: `Ollama.Enabled=true`이고 `OllamaInstances`에 이름 "ollama"가 이미 있을 때, `BaseURL`과 `ContextSize`가 모두 일치하면 idempotent(재promote 없이 통과), 하나라도 다르면 error. Vllm은 `Endpoint` 기준. 이렇게 하면 `LoadEdge` → `validateEdgeConfig` → `LoadFromConfig` 이중 normalize 경로에서 error 없이 통과하면서, raw config에서 진짜 충돌하는 경우는 error를 낸다.
|
||||
- 헬퍼를 `hasInstanceName`/`hasVllmInstanceName`(단순 존재 확인)에서 `findOllamaInstance`/`findVllmInstance`(포인터 반환)로 교체해 충돌 값 비교까지 한 번에 처리했다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
- 상태: 차단
|
||||
- 사유 유형: 사용자 소유 외부 환경 준비 필요
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: split-host smoke 증거(Edge `/v1/models` multi-target 노출, inference 두 개, CLI 호출)는 원격 runner(`toki-labs.com`)와 연결된 Node, Node token, Ollama endpoint가 필요하다. 현재 local 작업 컨테이너에서 접근 불가.
|
||||
- 실행한 검증/명령:
|
||||
- `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd` → 전체 PASS
|
||||
- `go test -count=1 ./...` → 전체 PASS
|
||||
- `go build ./apps/edge/... ./apps/node/...` → 성공
|
||||
- 자동 후속 불가 이유: 원격 Edge 주소(`toki-labs.com:18081`), 연결 Node, Node token, Ollama endpoint는 사용자 소유 환경이며 현재 컨테이너에서 SSH 접근 불가.
|
||||
- 재개 조건:
|
||||
1. 원격 runner에서 Edge와 Node가 실행 중이고, multi-target Edge config(inference 두 개 + CLI 한 개)가 적용되어 있어야 한다.
|
||||
2. `curl -fsS http://toki-labs.com:18081/v1/models` 응답에 두 inference route가 보여야 한다.
|
||||
3. 동일 Node id로 inference 호출 두 개와 CLI 호출 한 개가 성공해야 한다.
|
||||
4. 위 증거를 `검증 결과` 섹션에 채운 뒤 코드리뷰를 재실행한다.
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- same-name explicit instance가 exact promoted legacy 값과 다르면 error가 난다.
|
||||
- already normalized config를 다시 normalize해도 duplicate가 생기지 않는다.
|
||||
- `node.LoadFromConfig` raw legacy path가 Ollama/Vllm adapter를 payload까지 유지한다.
|
||||
- 원격 smoke가 실행된 경우 `/v1/models`, inference 두 개, CLI 호출이 같은 connected Node id/alias를 사용한다.
|
||||
- 원격 smoke가 차단된 경우 blocker가 user-owned prerequisite인지와 재개 조건이 구체적인지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### REVIEW_TEST-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd
|
||||
ok iop/packages/go/config 0.018s
|
||||
ok iop/apps/edge/internal/node 0.004s
|
||||
ok iop/apps/edge/internal/transport 2.016s
|
||||
ok iop/apps/edge/internal/edgecmd 0.007s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./...
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.135s
|
||||
ok iop/apps/control-plane/internal/wire 1.477s
|
||||
ok iop/apps/edge/cmd/edge 0.042s
|
||||
ok iop/apps/edge/internal/bootstrap 0.019s
|
||||
ok iop/apps/edge/internal/controlplane 4.456s
|
||||
ok iop/apps/edge/internal/edgecmd 0.010s
|
||||
ok iop/apps/edge/internal/events 0.004s
|
||||
ok iop/apps/edge/internal/input 0.010s
|
||||
ok iop/apps/edge/internal/input/a2a 0.006s
|
||||
ok iop/apps/edge/internal/node 0.008s
|
||||
ok iop/apps/edge/internal/openai 1.508s
|
||||
ok iop/apps/edge/internal/opsconsole 0.011s
|
||||
ok iop/apps/edge/internal/service 0.013s
|
||||
ok iop/apps/edge/internal/transport 2.019s
|
||||
ok iop/apps/node/cmd/node 0.011s
|
||||
ok iop/apps/node/internal/adapters 0.009s
|
||||
ok iop/apps/node/internal/adapters/cli 42.153s
|
||||
ok iop/apps/node/internal/adapters/cli/status 39.808s
|
||||
ok iop/apps/node/internal/adapters/ollama 0.008s
|
||||
ok iop/apps/node/internal/adapters/vllm 0.014s
|
||||
ok iop/apps/node/internal/bootstrap 0.264s
|
||||
ok iop/apps/node/internal/node 0.120s
|
||||
ok iop/apps/node/internal/router 0.005s
|
||||
ok iop/apps/node/internal/store 0.045s
|
||||
ok iop/apps/node/internal/terminal 0.443s
|
||||
ok iop/apps/node/internal/transport 5.145s
|
||||
ok iop/packages/go/audit 0.002s
|
||||
ok iop/packages/go/config 0.013s
|
||||
ok iop/packages/go/hostsetup 0.003s
|
||||
ok iop/packages/go/observability 0.009s
|
||||
```
|
||||
|
||||
```bash
|
||||
$ go build ./apps/edge/... ./apps/node/...
|
||||
(exit 0, no output)
|
||||
```
|
||||
|
||||
원격 smoke는 사용자 소유 환경 차단으로 미실행. `사용자 리뷰 요청` 참조.
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/healthz
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/v1/models
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-a>","messages":[{"role":"user","content":"ping"}]}'
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-b>","messages":[{"role":"user","content":"ping"}]}'
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ <edge-cli-command-for-cli-target>
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
Sections and their ownership:
|
||||
|
||||
| 섹션 | 소유자 | 설명 |
|
||||
|------|--------|------|
|
||||
| 헤더 주석, 개요(date/task/plan/tag), 리뷰 에이전트 지시 | 스텁 생성 시 고정 | 구현 에이전트가 수정하거나 실행하지 않음 |
|
||||
| Roadmap Targets | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 수정하지 않음; PASS 시 code-review가 `complete.log`의 `Roadmap Completion`으로 복사 |
|
||||
| 구현 항목별 완료 여부 (항목명) | 스텁 생성 시 고정 | `[ ]` → `[x]` 체크만 구현 에이전트가 수행 |
|
||||
| 구현 체크리스트 (항목 텍스트/순서) | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 `[ ]` → `[x]` 체크만 수행; 마지막 체크박스는 저장 전 필수 |
|
||||
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
|
||||
| 계획 대비 변경 사항, 주요 설계 결정 | 구현 에이전트가 채움 | placeholder 텍스트를 실제 내용으로 교체 |
|
||||
| 사용자 리뷰 요청 | 구현 에이전트가 채움 | 진행에 사용자 입력이 필요하지 않으면 `상태: 없음` 유지; 구현 중 직접 질문은 금지; 필요하면 결정 항목, 근거, 명령 출력, 자동 후속 불가 이유, 재개 조건을 기록 |
|
||||
| 리뷰어를 위한 체크포인트 | 스텁 생성 시 고정 | 계획에서 추출한 리뷰 포인트 |
|
||||
| 검증 결과 (섹션 제목 + 명령) | 스텁 생성 시 고정 | 실행 출력만 구현 에이전트가 채움; 명령 변경은 `계획 대비 변경 사항`에 기록 |
|
||||
| 코드리뷰 결과 | 리뷰 에이전트가 append | 스텁에 포함하지 않음 |
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: FAIL
|
||||
- 차원별 평가:
|
||||
- correctness: Fail
|
||||
- completeness: Fail
|
||||
- test coverage: Fail
|
||||
- API contract: Fail
|
||||
- code quality: Pass
|
||||
- plan deviation: Fail
|
||||
- verification trust: Pass
|
||||
- 발견된 문제:
|
||||
- Required: `packages/go/config/config.go:319`와 `packages/go/config/config.go:331`에서 existing same-name instance의 `Enabled` 값을 비교하지 않습니다. 후속 계획은 legacy enabled config와 같은 이름의 explicit instance가 raw config에 함께 있을 때 “disabled 또는 다른 endpoint/base_url/context_size 충돌”을 거부하라고 했지만, 현재 구현은 `BaseURL`/`ContextSize` 또는 `Endpoint`만 비교합니다. 따라서 `ollama.enabled=true`와 `ollama_instances: [{name: "ollama", enabled: false, base_url: same, context_size: same}]` 조합은 error 없이 통과하고, 이후 `BuildConfigPayload`는 disabled explicit instance를 skip해 enabled legacy adapter를 payload에서 누락합니다. Vllm도 같은 문제가 있습니다. `Enabled`가 legacy promoted 값과 일치하는지도 비교하고, disabled same-name explicit instance 회귀 테스트를 Ollama/Vllm 양쪽에 추가하세요.
|
||||
- 다음 단계: WARN/FAIL follow-up으로 active plan/review를 다시 작성한다. 원격 field smoke blocker는 user-owned로 보이나, repo-owned normalize correctness issue가 아직 남아 있어 이번 루프에서는 `USER_REVIEW.md`를 만들지 않는다.
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
<!-- task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke plan=2 tag=REVIEW_REVIEW_TEST -->
|
||||
|
||||
# Code Review Reference - REVIEW_REVIEW_TEST
|
||||
|
||||
> **[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 user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts 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 the needed decision in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-11
|
||||
task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke, plan=2, tag=REVIEW_REVIEW_TEST
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- Task ids:
|
||||
- `field-smoke`: split-host smoke에서 하나의 연결된 Node가 최소 두 개의 inference model target과 하나의 CLI target을 같은 Edge-Node 연결로 호출할 수 있음을 확인한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md` → `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` → `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [REVIEW_REVIEW_TEST-1] Enabled mismatch conflict | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] `NormalizeAdapters`가 legacy enabled config와 same-name explicit disabled instance를 Ollama/Vllm 각각 error로 거부하게 한다. (`!existing.Enabled` 조건 추가)
|
||||
- [x] disabled same-name explicit instance 회귀 테스트를 Ollama/Vllm 각각 추가한다. (`TestNormalizeAdapters_OllamaDisabledSameNameErrors`, `TestNormalizeAdapters_VllmDisabledSameNameErrors`)
|
||||
- [x] idempotent 재호출 테스트가 여전히 통과하도록 exact promoted legacy instance는 enabled 값까지 동일할 때만 허용한다. (promoted instance는 `Enabled=true`이므로 재호출 시 일치 → 통과)
|
||||
- [x] `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`, `go test -count=1 ./...`, `go build ./apps/edge/... ./apps/node/...`를 실행하고 실제 stdout/stderr를 기록한다.
|
||||
- [x] 원격 field 환경이 준비되어 있지 않아 `사용자 리뷰 요청`에 기록한다.
|
||||
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/`를 `agent-task/archive/YYYY/MM/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-node-multi-target-serving-foundation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [x] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [x] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
없음. 체크리스트 전 항목을 계획대로 구현했다.
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `!existing.Enabled` 조건을 충돌 감지에 추가해 legacy `Enabled=true` + explicit `Enabled=false` same-name 조합을 error로 처리. idempotent 경로(promoted legacy → `Enabled=true`)와 구분된다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
- 상태: 차단
|
||||
- 사유 유형: 사용자 소유 외부 환경 준비 필요
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: split-host smoke 증거(Edge `/v1/models` multi-target 노출, inference 두 개, CLI 호출)는 원격 runner(`toki-labs.com`)와 연결된 Node, Node token, Ollama endpoint가 필요하다. 현재 local 작업 컨테이너에서 접근 불가.
|
||||
- 실행한 검증/명령:
|
||||
- `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd` → 전체 PASS
|
||||
- `go test -count=1 ./...` → 전체 PASS
|
||||
- `go build ./apps/edge/... ./apps/node/...` → 성공
|
||||
- 자동 후속 불가 이유: 원격 Edge 주소(`toki-labs.com:18081`), 연결 Node, Node token, Ollama endpoint는 사용자 소유 환경이며 현재 컨테이너에서 SSH 접근 불가.
|
||||
- 재개 조건:
|
||||
1. 원격 runner에서 Edge와 Node가 실행 중이고, multi-target Edge config(inference 두 개 + CLI 한 개)가 적용되어 있어야 한다.
|
||||
2. `curl -fsS http://toki-labs.com:18081/v1/models` 응답에 두 inference route가 보여야 한다.
|
||||
3. 동일 Node id로 inference 호출 두 개와 CLI 호출 한 개가 성공해야 한다.
|
||||
4. 위 증거를 `검증 결과` 섹션에 채운 뒤 코드리뷰를 재실행한다.
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- legacy enabled config와 same-name explicit disabled instance가 Ollama/Vllm 모두 error로 거부된다.
|
||||
- idempotent 재호출은 enabled 값까지 동일한 promoted legacy instance에서만 통과한다.
|
||||
- 원격 smoke가 실행된 경우 `/v1/models`, inference 두 개, CLI 호출이 같은 connected Node id/alias를 사용한다.
|
||||
- 원격 smoke가 차단된 경우 blocker가 user-owned prerequisite인지와 재개 조건이 구체적인지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### REVIEW_REVIEW_TEST-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd
|
||||
ok iop/packages/go/config 0.016s
|
||||
ok iop/apps/edge/internal/node 0.004s
|
||||
ok iop/apps/edge/internal/transport 2.010s
|
||||
ok iop/apps/edge/internal/edgecmd 0.007s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./...
|
||||
ok iop/apps/control-plane/cmd/control-plane 0.149s
|
||||
ok iop/apps/control-plane/internal/wire 1.479s
|
||||
ok iop/apps/edge/cmd/edge 0.035s
|
||||
ok iop/apps/edge/internal/bootstrap 0.017s
|
||||
ok iop/apps/edge/internal/controlplane 4.447s
|
||||
ok iop/apps/edge/internal/edgecmd 0.008s
|
||||
ok iop/apps/edge/internal/events 0.003s
|
||||
ok iop/apps/edge/internal/input 0.005s
|
||||
ok iop/apps/edge/internal/input/a2a 0.005s
|
||||
ok iop/apps/edge/internal/node 0.004s
|
||||
ok iop/apps/edge/internal/openai 1.508s
|
||||
ok iop/apps/edge/internal/opsconsole 0.007s
|
||||
ok iop/apps/edge/internal/service 0.006s
|
||||
ok iop/apps/edge/internal/transport 2.017s
|
||||
ok iop/apps/node/cmd/node 0.010s
|
||||
ok iop/apps/node/internal/adapters 0.008s
|
||||
ok iop/apps/node/internal/adapters/cli 42.105s
|
||||
ok iop/apps/node/internal/adapters/cli/status 39.816s
|
||||
ok iop/apps/node/internal/adapters/ollama 0.016s
|
||||
ok iop/apps/node/internal/adapters/vllm 0.015s
|
||||
ok iop/apps/node/internal/bootstrap 0.263s
|
||||
ok iop/apps/node/internal/node 0.120s
|
||||
ok iop/apps/node/internal/router 0.004s
|
||||
ok iop/apps/node/internal/store 0.062s
|
||||
ok iop/apps/node/internal/terminal 0.438s
|
||||
ok iop/apps/node/internal/transport 5.139s
|
||||
ok iop/packages/go/audit 0.002s
|
||||
ok iop/packages/go/config 0.011s
|
||||
ok iop/packages/go/hostsetup 0.004s
|
||||
ok iop/packages/go/observability 0.009s
|
||||
```
|
||||
|
||||
```bash
|
||||
$ go build ./apps/edge/... ./apps/node/...
|
||||
(exit 0, no output)
|
||||
```
|
||||
|
||||
원격 smoke는 사용자 소유 환경 차단으로 미실행. `사용자 리뷰 요청` 참조.
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/healthz
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/v1/models
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-a>","messages":[{"role":"user","content":"ping"}]}'
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://toki-labs.com:18081/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-b>","messages":[{"role":"user","content":"ping"}]}'
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ <edge-cli-command-for-cli-target>
|
||||
(원격 환경 준비 후 수집 예정)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
Sections and their ownership:
|
||||
|
||||
| 섹션 | 소유자 | 설명 |
|
||||
|------|--------|------|
|
||||
| 헤더 주석, 개요(date/task/plan/tag), 리뷰 에이전트 지시 | 스텁 생성 시 고정 | 구현 에이전트가 수정하거나 실행하지 않음 |
|
||||
| Roadmap Targets | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 수정하지 않음; PASS 시 code-review가 `complete.log`의 `Roadmap Completion`으로 복사 |
|
||||
| 구현 항목별 완료 여부 (항목명) | 스텁 생성 시 고정 | `[ ]` → `[x]` 체크만 구현 에이전트가 수행 |
|
||||
| 구현 체크리스트 (항목 텍스트/순서) | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 `[ ]` → `[x]` 체크만 수행; 마지막 체크박스는 저장 전 필수 |
|
||||
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
|
||||
| 계획 대비 변경 사항, 주요 설계 결정 | 구현 에이전트가 채움 | placeholder 텍스트를 실제 내용으로 교체 |
|
||||
| 사용자 리뷰 요청 | 구현 에이전트가 채움 | 진행에 사용자 입력이 필요하지 않으면 `상태: 없음` 유지; 구현 중 직접 질문은 금지; 필요하면 결정 항목, 근거, 명령 출력, 자동 후속 불가 이유, 재개 조건을 기록 |
|
||||
| 리뷰어를 위한 체크포인트 | 스텁 생성 시 고정 | 계획에서 추출한 리뷰 포인트 |
|
||||
| 검증 결과 (섹션 제목 + 명령) | 스텁 생성 시 고정 | 실행 출력만 구현 에이전트가 채움; 명령 변경은 `계획 대비 변경 사항`에 기록 |
|
||||
| 코드리뷰 결과 | 리뷰 에이전트가 append | 스텁에 포함하지 않음 |
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: FAIL
|
||||
- 차원별 평가:
|
||||
- correctness: Pass
|
||||
- completeness: Fail
|
||||
- test coverage: Fail
|
||||
- API contract: Pass
|
||||
- code quality: Pass
|
||||
- plan deviation: Pass
|
||||
- verification trust: Pass
|
||||
- 발견된 문제:
|
||||
- Required: `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/CODE_REVIEW-cloud-G07.md:91`의 `사용자 리뷰 요청`대로 split-host field smoke 핵심 증거가 아직 없다. `/healthz`, `/v1/models`, 두 inference target 호출, CLI target 호출이 같은 connected Node id/alias를 통해 성공했다는 증거가 없으므로 Roadmap target `field-smoke`는 완료로 판정할 수 없다. 다만 남은 blocker는 repo-owned 코드 결함이 아니라 원격 runner, 연결된 Node, Node token, Ollama endpoint 준비라는 user-owned 외부 환경 prerequisite이다.
|
||||
- 다음 단계: USER_REVIEW stop state를 작성한다. 자동 follow-up plan으로 해결 가능한 repo-owned 이슈는 현재 남아 있지 않으며, 원격 field 환경 준비 후 동일 task를 재개해야 한다.
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# Complete - m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke
|
||||
|
||||
## 완료 일시
|
||||
|
||||
2026-06-12
|
||||
|
||||
## 요약
|
||||
|
||||
3회 plan/review loop 이후 USER_REVIEW stop state를 원격 Edge/Node field smoke 증거와 사용자 승인으로 해소했다. 최종 판정은 PASS/RESOLVED.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_cloud_G07_0.log` | `code_review_cloud_G07_0.log` | FAIL | legacy adapter normalize가 raw config conflict를 숨기는 repo-owned correctness issue 발견 |
|
||||
| `plan_cloud_G07_1.log` | `code_review_cloud_G07_1.log` | FAIL | same-name explicit disabled instance가 enabled legacy adapter를 가릴 수 있는 `Enabled` mismatch issue 발견 |
|
||||
| `plan_cloud_G07_2.log` | `code_review_cloud_G07_2.log` | FAIL | repo-owned normalize fixes/tests/build는 통과했으나 field smoke 원격 환경 prerequisite이 남아 USER_REVIEW로 전환 |
|
||||
| `USER_REVIEW.md` | user decision | PASS/RESOLVED | 사용자 제공 원격 Node 연결 후 두 Ollama inference route 개별/병렬 호출 성공 증거로 stop state 해소 |
|
||||
|
||||
## 구현/정리 내용
|
||||
|
||||
- `NormalizeAdapters`의 legacy enabled config와 same-name explicit disabled instance 충돌을 Ollama/Vllm 양쪽에서 error로 거부하도록 보정했다.
|
||||
- disabled same-name explicit instance 회귀 테스트와 legacy raw config payload 유지 테스트를 추가했다.
|
||||
- 사용자 제공 원격 환경에 Mac용 node binary/bootstrap을 준비하고, Edge config에 두 Ollama route를 적용해 field smoke를 수행했다.
|
||||
- 원격 smoke 완료 후 사용자 요청에 따라 Edge 서버를 종료했다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
- `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd` - PASS; 상세 stdout은 `code_review_cloud_G07_2.log`의 `검증 결과` 섹션에 보관됨.
|
||||
- `go test -count=1 ./...` - PASS; 상세 stdout은 `code_review_cloud_G07_2.log`의 `검증 결과` 섹션에 보관됨.
|
||||
- `go build ./apps/edge/... ./apps/node/...` - PASS; exit 0, output 없음.
|
||||
- `curl -fsS http://toki-labs.com:18081/healthz` - PASS; `{"status":"ok"}`.
|
||||
- `curl -fsS http://toki-labs.com:18081/v1/models` - PASS; `gemma4_ext:26b`, `gemma4:26b` 노출 확인.
|
||||
- `curl -sS --max-time 180 http://toki-labs.com:18081/v1/chat/completions ... model=gemma4_ext:26b` - PASS; 응답 content `ext-ok`.
|
||||
- `curl -sS --max-time 180 http://toki-labs.com:18081/v1/chat/completions ... model=gemma4:26b` - PASS; 응답 content `base-ok`.
|
||||
- `node-ollama-1.runtime.concurrency=2` 적용 후 두 `curl ... /v1/chat/completions` 병렬 호출 - PASS; `gemma4_ext:26b` 응답 `ext-parallel-ok`, `gemma4:26b` 응답 `base-parallel-ok`.
|
||||
- `tail -n 80 edge.log | grep -i "concurrency\\|limit exceeded"` - PASS; 동시성 조정 후 관련 오류 없음.
|
||||
- `curl -sS --max-time 5 http://toki-labs.com:18081/healthz` after shutdown - PASS; 사용자 요청에 따라 Edge 서버 종료 후 연결 실패 확인.
|
||||
|
||||
## Roadmap Completion
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- Completed task ids:
|
||||
- `field-smoke`: PASS/RESOLVED; evidence=`plan_cloud_G07_2.log`, `code_review_cloud_G07_2.log`, `USER_REVIEW.md`; verification=`go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`, `go test -count=1 ./...`, `go build ./apps/edge/... ./apps/node/...`, 원격 Edge `/healthz`, `/v1/models`, 두 Ollama inference route 개별/병렬 호출
|
||||
- Not completed task ids: 없음
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
||||
## 후속 작업
|
||||
|
||||
- 없음
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
<!-- task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke plan=1 tag=REVIEW_TEST -->
|
||||
|
||||
# Plan - REVIEW_TEST
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 작업은 직전 code review `FAIL`의 Required 이슈만 먼저 고친 뒤 field smoke 종결 상태를 다시 판단하는 후속 작업이다. 사용자에게 직접 질문하지 말고, 원격 서버/노드/token/모델 endpoint 준비가 없어 smoke를 끝까지 실행할 수 없으면 `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청`에 정확한 증거와 재개 조건을 남긴다.
|
||||
|
||||
## 배경
|
||||
|
||||
직전 구현은 `node.LoadFromConfig`에서도 legacy adapter config를 normalize하도록 보정했지만, idempotence를 위해 추가한 same-name guard가 첫 normalize와 재호출을 구분하지 못한다. raw config에 legacy `ollama`/`vllm`과 같은 이름의 explicit instance가 함께 있으면 legacy adapter가 조용히 무시될 수 있어, field smoke 전에 repo-owned correctness issue를 먼저 닫아야 한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
원격 runner, 연결된 Node, Node token, Ollama endpoint, CLI target 같은 사용자 소유 prerequisite이 없으면 진행할 수 없는 경우 active review stub의 `사용자 리뷰 요청`에 기록한다. code-review가 user-review stop 여부를 결정한다. 단, normalize correctness issue와 테스트 증거 공백은 repo-owned 후속 작업으로 해결한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- Task ids:
|
||||
- `field-smoke`: split-host smoke에서 하나의 연결된 Node가 최소 두 개의 inference model target과 하나의 CLI target을 같은 Edge-Node 연결로 호출할 수 있음을 확인한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/plan_cloud_G07_0.log`
|
||||
- `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/code_review_cloud_G07_0.log`
|
||||
- `packages/go/config/config.go`
|
||||
- `packages/go/config/config_test.go`
|
||||
- `apps/edge/internal/node/store.go`
|
||||
- `apps/edge/internal/node/store_test.go`
|
||||
- `apps/edge/internal/node/mapper.go`
|
||||
- `apps/edge/internal/transport/server_test.go`
|
||||
- `apps/edge/internal/edgecmd/config.go`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-test/local/testing-smoke.md`
|
||||
|
||||
### 리뷰 실패 요약
|
||||
|
||||
- Required: `NormalizeAdapters`가 `hasInstanceName(..., "ollama")` / `hasVllmInstanceName(..., "vllm")`만으로 legacy promotion을 건너뛰어 raw config의 conflicting same-name instance를 조용히 허용한다.
|
||||
- 영향: `BuildConfigPayload`는 normalized slice만 읽으므로, explicit same-name instance가 disabled이거나 legacy field와 값이 다르면 enabled legacy adapter가 Node payload에서 빠질 수 있다.
|
||||
- `go test -count=1 ./...`와 `go build ./apps/edge/... ./apps/node/...`는 통과했지만, 해당 edge case를 직접 검증하는 테스트가 없다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 이번 후속 범위는 normalize idempotence/conflict handling과 그 회귀 테스트로 제한한다.
|
||||
- field smoke 자체는 이 fix 후에도 원격 환경이 준비되어야 완료 가능하다.
|
||||
- docs, roadmap, archive 문서는 수정하지 않는다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build: `cloud-G07`, review: `cloud-G07`. Required fix는 deterministic code/test work지만, 이 subtask의 종료 판단은 원격 field smoke와 user-review gate가 함께 걸려 있다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `NormalizeAdapters`가 이미 normalize된 config를 재호출해도 중복 promote하지 않는 회귀 테스트를 Ollama/Vllm 각각 추가한다.
|
||||
- [ ] legacy enabled config와 같은 이름의 explicit instance가 raw config에 함께 있을 때 disabled 또는 다른 endpoint/base_url/context_size 충돌을 조용히 허용하지 않는 회귀 테스트를 Ollama/Vllm 각각 추가한다.
|
||||
- [ ] `NormalizeAdapters`를 수정해 exact promoted legacy instance 재호출은 idempotent하게 허용하고, 첫 normalize의 same-name conflicting explicit instance는 error로 반환하게 한다.
|
||||
- [ ] `node.LoadFromConfig`가 raw legacy `NodeDefinition`을 받아도 normalized copy를 저장해 `BuildConfigPayload`에 Ollama/Vllm adapter가 포함되는지 검증한다.
|
||||
- [ ] `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`, `go test -count=1 ./...`, `go build ./apps/edge/... ./apps/node/...`를 실행하고 실제 stdout/stderr를 기록한다.
|
||||
- [ ] 원격 field 환경이 준비되어 있으면 `/healthz`, `/v1/models`, inference target 두 개 호출, CLI target 호출이 같은 connected Node id/alias를 쓰는 증거를 수집한다. 준비되어 있지 않으면 `사용자 리뷰 요청`에 user-owned blocker와 재개 조건을 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [REVIEW_TEST-1] NormalizeAdapters same-name conflict and smoke unblock
|
||||
|
||||
- 문제: idempotence guard가 raw config conflict를 숨겨 enabled legacy adapter가 payload에서 사라질 수 있다.
|
||||
- 해결 방법: promoted legacy instance와 기존 same-name instance를 비교하는 helper를 두고, 값이 정확히 같으면 재호출로 간주해 skip한다. 값이 다르거나 disabled 상태가 충돌하면 error를 반환한다. Vllm도 같은 규칙을 적용한다.
|
||||
- 수정 파일 및 체크리스트:
|
||||
- [ ] `packages/go/config/config.go`: legacy promotion conflict 판정과 idempotent 재호출 허용을 구현한다.
|
||||
- [ ] `packages/go/config/config_test.go`: LoadEdge 기준 idempotence와 same-name conflict 회귀 테스트를 추가한다.
|
||||
- [ ] `apps/edge/internal/node/store_test.go`: raw `NodeDefinition`을 `LoadFromConfig`에 넣는 경로에서 legacy adapter가 payload까지 유지되는지 검증한다.
|
||||
- [ ] 필요 시 `apps/edge/internal/transport/server_test.go`의 기존 oneof 테스트를 새 normalize 계약과 맞춘다.
|
||||
- 테스트 작성: 필수. 이번 Required 이슈는 기존 테스트가 놓친 edge case이므로 테스트 없이 수정하지 않는다.
|
||||
- 중간 검증:
|
||||
- `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `packages/go/config/config.go` | REVIEW_TEST-1 |
|
||||
| `packages/go/config/config_test.go` | REVIEW_TEST-1 |
|
||||
| `apps/edge/internal/node/store_test.go` | REVIEW_TEST-1 |
|
||||
| `apps/edge/internal/transport/server_test.go` | REVIEW_TEST-1 |
|
||||
| `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/CODE_REVIEW-cloud-G07.md` | REVIEW_TEST-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd
|
||||
go test -count=1 ./...
|
||||
go build ./apps/edge/... ./apps/node/...
|
||||
curl -fsS http://<edge-openai>/healthz
|
||||
curl -fsS http://<edge-openai>/v1/models
|
||||
curl -fsS http://<edge-openai>/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-a>","messages":[{"role":"user","content":"ping"}]}'
|
||||
curl -fsS http://<edge-openai>/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-b>","messages":[{"role":"user","content":"ping"}]}'
|
||||
```
|
||||
|
||||
CLI target 호출은 프로젝트의 최신 Edge command endpoint 또는 `iop-edge` command로 실행하고, 실제 명령과 출력을 `CODE_REVIEW-cloud-G07.md`에 기록한다. 원격 환경이 사용자 소유 prerequisite 때문에 불가하면 실행하지 못한 field smoke 명령과 정확한 blocker를 `사용자 리뷰 요청`에 기록한다.
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<!-- task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke plan=2 tag=REVIEW_REVIEW_TEST -->
|
||||
|
||||
# Plan - REVIEW_REVIEW_TEST
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 작업은 직전 code review `FAIL`의 남은 Required 이슈만 고친다. 사용자에게 직접 질문하지 말고, 원격 field smoke가 여전히 사용자 소유 환경 때문에 불가능하면 `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청`에 증거와 재개 조건을 남긴다.
|
||||
|
||||
## 배경
|
||||
|
||||
직전 후속 구현은 `NormalizeAdapters`의 same-name conflict에서 base URL, context size, endpoint mismatch는 잡았지만 `Enabled` mismatch를 비교하지 않았다. legacy field가 enabled인데 explicit same-name instance가 disabled이면 normalize는 통과하고, `BuildConfigPayload`는 disabled instance를 skip해 enabled legacy adapter가 payload에서 사라질 수 있다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
원격 runner, 연결된 Node, Node token, Ollama endpoint, CLI target 같은 사용자 소유 prerequisite이 없으면 진행할 수 없는 경우 active review stub의 `사용자 리뷰 요청`에 기록한다. code-review가 user-review stop 여부를 결정한다. repo-owned normalize correctness issue와 테스트 증거 공백은 사용자 리뷰 요청으로 넘기지 않는다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- Task ids:
|
||||
- `field-smoke`: split-host smoke에서 하나의 연결된 Node가 최소 두 개의 inference model target과 하나의 CLI target을 같은 Edge-Node 연결로 호출할 수 있음을 확인한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/plan_cloud_G07_1.log`
|
||||
- `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/code_review_cloud_G07_1.log`
|
||||
- `packages/go/config/config.go`
|
||||
- `packages/go/config/config_test.go`
|
||||
- `apps/edge/internal/node/store_test.go`
|
||||
- `apps/edge/internal/node/mapper.go`
|
||||
|
||||
### 리뷰 실패 요약
|
||||
|
||||
- Required: `NormalizeAdapters`가 same-name explicit instance의 `Enabled` mismatch를 conflict로 처리하지 않는다.
|
||||
- 영향: raw config에 `ollama.enabled=true`와 `ollama_instances[].name="ollama", enabled=false`가 같은 URL/context로 함께 있으면 adapter가 payload에서 누락될 수 있다. Vllm도 동일하다.
|
||||
- 기존 테스트는 다른 URL/endpoint만 검증하고 disabled same-name instance를 직접 검증하지 않는다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 이번 후속 범위는 `Enabled` conflict handling과 disabled explicit instance 회귀 테스트로 제한한다.
|
||||
- 이미 추가된 legacy payload 유지 테스트와 idempotence 테스트는 유지한다.
|
||||
- field smoke 자체는 이 fix 후에도 원격 환경이 준비되어야 완료 가능하다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build: `cloud-G07`, review: `cloud-G07`. repo-owned fix는 좁지만 이 subtask의 종료 판단은 원격 field smoke와 user-review gate가 함께 걸려 있다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `NormalizeAdapters`가 legacy enabled config와 same-name explicit disabled instance를 Ollama/Vllm 각각 error로 거부하게 한다.
|
||||
- [ ] disabled same-name explicit instance 회귀 테스트를 Ollama/Vllm 각각 추가한다.
|
||||
- [ ] idempotent 재호출 테스트가 여전히 통과하도록 exact promoted legacy instance는 enabled 값까지 동일할 때만 허용한다.
|
||||
- [ ] `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`, `go test -count=1 ./...`, `go build ./apps/edge/... ./apps/node/...`를 실행하고 실제 stdout/stderr를 기록한다.
|
||||
- [ ] 원격 field 환경이 준비되어 있으면 `/healthz`, `/v1/models`, inference target 두 개 호출, CLI target 호출이 같은 connected Node id/alias를 쓰는 증거를 수집한다. 준비되어 있지 않으면 `사용자 리뷰 요청`에 user-owned blocker와 재개 조건을 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [REVIEW_REVIEW_TEST-1] Enabled mismatch conflict
|
||||
|
||||
- 문제: same-name explicit instance가 disabled이면 enabled legacy adapter가 silently dropped될 수 있다.
|
||||
- 해결 방법: `findOllamaInstance`/`findVllmInstance`로 찾은 explicit instance가 legacy promoted instance와 `Enabled`까지 정확히 일치하는지 비교한다. `Enabled`, `BaseURL`, `ContextSize`, `Endpoint` 중 하나라도 다르면 error를 반환한다.
|
||||
- 수정 파일 및 체크리스트:
|
||||
- [ ] `packages/go/config/config.go`: same-name legacy/explicit conflict 비교에 `Enabled`를 포함한다.
|
||||
- [ ] `packages/go/config/config_test.go`: disabled explicit same-name Ollama/Vllm conflict 테스트를 추가한다.
|
||||
- [ ] 필요 시 `apps/edge/internal/node/store_test.go`의 payload 유지 테스트를 새 계약과 맞춰 보강한다.
|
||||
- 테스트 작성: 필수. disabled same-name conflict가 이번 Required issue의 본체다.
|
||||
- 중간 검증:
|
||||
- `go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd`
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `packages/go/config/config.go` | REVIEW_REVIEW_TEST-1 |
|
||||
| `packages/go/config/config_test.go` | REVIEW_REVIEW_TEST-1 |
|
||||
| `agent-task/m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke/CODE_REVIEW-cloud-G07.md` | REVIEW_REVIEW_TEST-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/edgecmd
|
||||
go test -count=1 ./...
|
||||
go build ./apps/edge/... ./apps/node/...
|
||||
curl -fsS http://<edge-openai>/healthz
|
||||
curl -fsS http://<edge-openai>/v1/models
|
||||
curl -fsS http://<edge-openai>/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-a>","messages":[{"role":"user","content":"ping"}]}'
|
||||
curl -fsS http://<edge-openai>/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-b>","messages":[{"role":"user","content":"ping"}]}'
|
||||
```
|
||||
|
||||
CLI target 호출은 프로젝트의 최신 Edge command endpoint 또는 `iop-edge` command로 실행하고, 실제 명령과 출력을 `CODE_REVIEW-cloud-G07.md`에 기록한다. 원격 환경이 사용자 소유 prerequisite 때문에 불가하면 실행하지 못한 field smoke 명령과 정확한 blocker를 `사용자 리뷰 요청`에 기록한다.
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
<!-- task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke plan=0 tag=TEST -->
|
||||
|
||||
# Code Review Reference - TEST
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> Record the exact remote/local environment, commands, stdout/stderr, and any artifact paths.
|
||||
> If blocked by user-owned server, node, token, secret, or model endpoint preparation, fill `사용자 리뷰 요청` and stop.
|
||||
> Do not ask the user directly.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-10
|
||||
task=m-node-multi-target-serving-foundation/08+03,04,05,06,07_field_smoke, plan=0, tag=TEST
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- Task ids:
|
||||
- `field-smoke`: split-host smoke에서 하나의 연결된 Node가 최소 두 개의 inference model target과 하나의 CLI target을 같은 Edge-Node 연결로 호출할 수 있음을 확인한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.
|
||||
|
||||
smoke 증거가 같은 Edge-Node 연결과 같은 Node id를 가리키는지 확인한다. 환경 blocker가 repo-owned인지 user-owned인지 구분한다.
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [TEST-1] Split Host Multi Target Smoke | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] split-host smoke 사전 조건과 실제 실행 config를 기록한다.
|
||||
- [ ] Edge `/v1/models`가 두 inference model target을 노출하는 증거를 수집한다.
|
||||
- [ ] 같은 연결된 Node를 통해 inference model target 두 개를 각각 호출하는 증거를 수집한다.
|
||||
- [ ] 같은 연결된 Node를 통해 CLI target 하나를 호출하는 증거를 수집한다.
|
||||
- [ ] 실패 시 repo-owned fix와 user-owned prerequisite을 구분해 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] `코드리뷰 결과`에 판정을 append한다.
|
||||
- [ ] active `CODE_REVIEW-cloud-G07.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-cloud-G07.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 archive 이동한다.
|
||||
- [ ] WARN/FAIL이면 후속 상태 또는 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `/v1/models`에 두 inference route가 실제로 보인다.
|
||||
- 두 inference 호출과 CLI 호출이 같은 connected Node id/alias를 사용한다.
|
||||
- secret/token 값이 문서에 노출되지 않았다.
|
||||
- 실패가 있으면 repo-owned follow-up과 user-owned blocker가 분리되어 있다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### TEST-1 중간 검증
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./...
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://<edge-openai>/healthz
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://<edge-openai>/v1/models
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://<edge-openai>/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-a>","messages":[{"role":"user","content":"ping"}]}'
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -fsS http://<edge-openai>/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"<model-b>","messages":[{"role":"user","content":"ping"}]}'
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ <edge-cli-command-for-cli-target>
|
||||
(output)
|
||||
```
|
||||
|
|
@ -95,13 +95,17 @@ func LoadFromConfig(defs []config.NodeDefinition) (*NodeStore, error) {
|
|||
if agentKind == "" {
|
||||
agentKind = config.AgentKindGenericNode
|
||||
}
|
||||
adapters := d.Adapters
|
||||
if err := config.NormalizeAdapters(&adapters); err != nil {
|
||||
return nil, fmt.Errorf("node[%d] alias=%q: adapters: %w", i, d.Alias, err)
|
||||
}
|
||||
s.Add(&NodeRecord{
|
||||
ID: nodeID,
|
||||
Alias: d.Alias,
|
||||
Token: d.Token,
|
||||
AgentKind: agentKind,
|
||||
Index: i,
|
||||
Adapters: d.Adapters,
|
||||
Adapters: adapters,
|
||||
Runtime: d.Runtime,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,72 @@ func TestLoadFromConfig_DuplicateTokenWithAgentKind(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadFromConfig_LegacyOllamaInPayload(t *testing.T) {
|
||||
store, err := edgenode.LoadFromConfig([]config.NodeDefinition{
|
||||
{
|
||||
ID: "node-ollama",
|
||||
Alias: "ollama-node",
|
||||
Token: "token-ollama",
|
||||
Adapters: config.AdaptersConf{
|
||||
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434"},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("load from config: %v", err)
|
||||
}
|
||||
rec, ok := store.FindByToken("token-ollama")
|
||||
if !ok {
|
||||
t.Fatal("expected record for token-ollama")
|
||||
}
|
||||
payload, err := edgenode.BuildConfigPayload(rec)
|
||||
if err != nil {
|
||||
t.Fatalf("build config payload: %v", err)
|
||||
}
|
||||
var found bool
|
||||
for _, a := range payload.GetAdapters() {
|
||||
if a.GetType() == "ollama" && a.GetOllama().GetBaseUrl() == "http://127.0.0.1:11434" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected ollama adapter in payload with correct base_url, got %v", payload.GetAdapters())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromConfig_LegacyVllmInPayload(t *testing.T) {
|
||||
store, err := edgenode.LoadFromConfig([]config.NodeDefinition{
|
||||
{
|
||||
ID: "node-vllm",
|
||||
Alias: "vllm-node",
|
||||
Token: "token-vllm",
|
||||
Adapters: config.AdaptersConf{
|
||||
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("load from config: %v", err)
|
||||
}
|
||||
rec, ok := store.FindByToken("token-vllm")
|
||||
if !ok {
|
||||
t.Fatal("expected record for token-vllm")
|
||||
}
|
||||
payload, err := edgenode.BuildConfigPayload(rec)
|
||||
if err != nil {
|
||||
t.Fatalf("build config payload: %v", err)
|
||||
}
|
||||
var found bool
|
||||
for _, a := range payload.GetAdapters() {
|
||||
if a.GetType() == "vllm" && a.GetVllm().GetEndpoint() == "http://127.0.0.1:8000" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected vllm adapter in payload with correct endpoint, got %v", payload.GetAdapters())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromConfig_AutoIDUnique(t *testing.T) {
|
||||
store, err := edgenode.LoadFromConfig([]config.NodeDefinition{
|
||||
{Alias: "alpha", Token: "token-alpha"},
|
||||
|
|
|
|||
|
|
@ -271,8 +271,12 @@ func TestBuildConfigPayload_CLICompletionMarker(t *testing.T) {
|
|||
func TestBuildConfigPayload_OllamaVllmOneof(t *testing.T) {
|
||||
rec := &edgenode.NodeRecord{
|
||||
Adapters: config.AdaptersConf{
|
||||
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://localhost:11434"},
|
||||
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://localhost:8000"},
|
||||
OllamaInstances: []config.OllamaInstanceConf{
|
||||
{Name: "ollama", Enabled: true, BaseURL: "http://localhost:11434"},
|
||||
},
|
||||
VllmInstances: []config.VllmInstanceConf{
|
||||
{Name: "vllm", Enabled: true, Endpoint: "http://localhost:8000"},
|
||||
},
|
||||
},
|
||||
}
|
||||
payload, err := edgenode.BuildConfigPayload(rec)
|
||||
|
|
|
|||
|
|
@ -300,25 +300,37 @@ func LoadEdge(cfgFile string) (*EdgeConfig, error) {
|
|||
return &cfg, nil
|
||||
}
|
||||
|
||||
// normalizeAdapters promotes legacy single-instance Ollama/Vllm fields into the
|
||||
// NormalizeAdapters promotes legacy single-instance Ollama/Vllm fields into the
|
||||
// typed instance slices and validates that all instance names are unique.
|
||||
func NormalizeAdapters(a *AdaptersConf) error {
|
||||
return normalizeAdapters(a)
|
||||
}
|
||||
|
||||
func normalizeAdapters(a *AdaptersConf) error {
|
||||
if a.Ollama.Enabled {
|
||||
legacy := OllamaInstanceConf{
|
||||
Name: "ollama",
|
||||
Enabled: a.Ollama.Enabled,
|
||||
BaseURL: a.Ollama.BaseURL,
|
||||
ContextSize: a.Ollama.ContextSize,
|
||||
existing := findOllamaInstance(a.OllamaInstances, "ollama")
|
||||
if existing == nil {
|
||||
a.OllamaInstances = append([]OllamaInstanceConf{{
|
||||
Name: "ollama",
|
||||
Enabled: a.Ollama.Enabled,
|
||||
BaseURL: a.Ollama.BaseURL,
|
||||
ContextSize: a.Ollama.ContextSize,
|
||||
}}, a.OllamaInstances...)
|
||||
} else if !existing.Enabled || existing.BaseURL != a.Ollama.BaseURL || existing.ContextSize != a.Ollama.ContextSize {
|
||||
return fmt.Errorf("ollama: legacy field conflicts with explicit instance %q: enabled/base_url/context_size mismatch", "ollama")
|
||||
}
|
||||
a.OllamaInstances = append([]OllamaInstanceConf{legacy}, a.OllamaInstances...)
|
||||
}
|
||||
if a.Vllm.Enabled {
|
||||
legacy := VllmInstanceConf{
|
||||
Name: "vllm",
|
||||
Enabled: a.Vllm.Enabled,
|
||||
Endpoint: a.Vllm.Endpoint,
|
||||
existing := findVllmInstance(a.VllmInstances, "vllm")
|
||||
if existing == nil {
|
||||
a.VllmInstances = append([]VllmInstanceConf{{
|
||||
Name: "vllm",
|
||||
Enabled: a.Vllm.Enabled,
|
||||
Endpoint: a.Vllm.Endpoint,
|
||||
}}, a.VllmInstances...)
|
||||
} else if !existing.Enabled || existing.Endpoint != a.Vllm.Endpoint {
|
||||
return fmt.Errorf("vllm: legacy field conflicts with explicit instance %q: enabled/endpoint mismatch", "vllm")
|
||||
}
|
||||
a.VllmInstances = append([]VllmInstanceConf{legacy}, a.VllmInstances...)
|
||||
}
|
||||
|
||||
if err := checkUniqueNames("ollama_instances", func(i int) string { return a.OllamaInstances[i].Name }, len(a.OllamaInstances)); err != nil {
|
||||
|
|
@ -330,6 +342,24 @@ func normalizeAdapters(a *AdaptersConf) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func findOllamaInstance(instances []OllamaInstanceConf, name string) *OllamaInstanceConf {
|
||||
for i := range instances {
|
||||
if instances[i].Name == name {
|
||||
return &instances[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findVllmInstance(instances []VllmInstanceConf, name string) *VllmInstanceConf {
|
||||
for i := range instances {
|
||||
if instances[i].Name == name {
|
||||
return &instances[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateOpenAIRoutes rejects duplicate and empty model ids in the route catalog.
|
||||
func validateOpenAIRoutes(routes []OpenAIRouteEntry) error {
|
||||
seen := make(map[string]struct{}, len(routes))
|
||||
|
|
|
|||
|
|
@ -1154,6 +1154,92 @@ openai:
|
|||
}
|
||||
}
|
||||
|
||||
// NormalizeAdapters regression tests
|
||||
|
||||
func TestNormalizeAdapters_OllamaIdempotent(t *testing.T) {
|
||||
a := config.AdaptersConf{
|
||||
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434", ContextSize: 4096},
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err != nil {
|
||||
t.Fatalf("first normalize: %v", err)
|
||||
}
|
||||
if len(a.OllamaInstances) != 1 {
|
||||
t.Fatalf("expected 1 instance after first normalize, got %d", len(a.OllamaInstances))
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err != nil {
|
||||
t.Fatalf("second normalize (idempotent): %v", err)
|
||||
}
|
||||
if len(a.OllamaInstances) != 1 {
|
||||
t.Fatalf("expected still 1 instance after second normalize, got %d", len(a.OllamaInstances))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeAdapters_VllmIdempotent(t *testing.T) {
|
||||
a := config.AdaptersConf{
|
||||
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"},
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err != nil {
|
||||
t.Fatalf("first normalize: %v", err)
|
||||
}
|
||||
if len(a.VllmInstances) != 1 {
|
||||
t.Fatalf("expected 1 instance after first normalize, got %d", len(a.VllmInstances))
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err != nil {
|
||||
t.Fatalf("second normalize (idempotent): %v", err)
|
||||
}
|
||||
if len(a.VllmInstances) != 1 {
|
||||
t.Fatalf("expected still 1 instance after second normalize, got %d", len(a.VllmInstances))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeAdapters_OllamaConflictErrors(t *testing.T) {
|
||||
a := config.AdaptersConf{
|
||||
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434"},
|
||||
OllamaInstances: []config.OllamaInstanceConf{
|
||||
{Name: "ollama", Enabled: true, BaseURL: "http://other-host:11434"},
|
||||
},
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err == nil {
|
||||
t.Fatal("expected error for conflicting legacy ollama and explicit instance with same name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeAdapters_VllmConflictErrors(t *testing.T) {
|
||||
a := config.AdaptersConf{
|
||||
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"},
|
||||
VllmInstances: []config.VllmInstanceConf{
|
||||
{Name: "vllm", Enabled: true, Endpoint: "http://other-host:8000"},
|
||||
},
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err == nil {
|
||||
t.Fatal("expected error for conflicting legacy vllm and explicit instance with same name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeAdapters_OllamaDisabledSameNameErrors(t *testing.T) {
|
||||
a := config.AdaptersConf{
|
||||
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434"},
|
||||
OllamaInstances: []config.OllamaInstanceConf{
|
||||
{Name: "ollama", Enabled: false, BaseURL: "http://127.0.0.1:11434"},
|
||||
},
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err == nil {
|
||||
t.Fatal("expected error for legacy enabled ollama conflicting with same-name disabled explicit instance")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeAdapters_VllmDisabledSameNameErrors(t *testing.T) {
|
||||
a := config.AdaptersConf{
|
||||
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"},
|
||||
VllmInstances: []config.VllmInstanceConf{
|
||||
{Name: "vllm", Enabled: false, Endpoint: "http://127.0.0.1:8000"},
|
||||
},
|
||||
}
|
||||
if err := config.NormalizeAdapters(&a); err == nil {
|
||||
t.Fatal("expected error for legacy enabled vllm conflicting with same-name disabled explicit instance")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_ControlPlaneOverride(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
|
|
|
|||
Loading…
Reference in a new issue