update roadmap for lemonade provider serving validation
This commit is contained in:
parent
953617b12d
commit
28945ee742
9 changed files with 1320 additions and 10 deletions
|
|
@ -13,7 +13,7 @@ Ollama, Lemonade, vLLM, SGLang 같은 provider별 상태 확인 방식 차이를
|
|||
|
||||
## 상태
|
||||
|
||||
[검토중]
|
||||
[완료]
|
||||
|
||||
## 승격 조건
|
||||
|
||||
|
|
@ -54,13 +54,13 @@ Provider별 동시 처리 한도를 IOP가 소유하고, 한도를 넘는 요청
|
|||
|
||||
## 완료 리뷰
|
||||
|
||||
- 상태: 요청됨
|
||||
- 상태: 승인됨
|
||||
- 요청일: 2026-06-15
|
||||
- 완료 근거: 모든 기능 Task가 PASS 완료되었고, 마지막 `queue-observe`는 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/05+04_queue_observe_snapshot/complete.log`에서 완료 근거와 검증을 남겼다.
|
||||
- 리뷰 필요:
|
||||
- [ ] 사용자가 완료 결과를 확인했다
|
||||
- [ ] archive 이동을 승인했다
|
||||
- 리뷰 코멘트: 사용자 최종 확인과 archive 승인 대기
|
||||
- [x] 사용자가 완료 결과를 확인했다
|
||||
- [x] archive 이동을 승인했다
|
||||
- 리뷰 코멘트: 2026-06-15 코드 레벨 종료 검토와 smoke 검증에서 차단 이슈가 없어 archive 이동을 승인받고 완료 처리했다.
|
||||
|
||||
## 범위 제외
|
||||
|
||||
|
|
@ -20,8 +20,8 @@ Ollama 경로가 안정화된 뒤, 그 결과를 기준선으로 삼아 Lemonade
|
|||
- 경로: `agent-roadmap/archive/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
|
||||
- 요약: 하나의 Node 연결이 여러 CLI profile, terminal gateway, Ollama/vLLM/SGLang 같은 추론 엔진 연결, 여러 model target을 동시에 제공할 수 있도록 config/proto/routing/runtime 기준선을 정리했고, 최종 코드 리뷰와 검증까지 통과해 archive했다.
|
||||
|
||||
- [검토중] Node provider 상태와 Capacity Queue 기반
|
||||
- 경로: `agent-roadmap/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md`
|
||||
- [완료] Node provider 상태와 Capacity Queue 기반
|
||||
- 경로: `agent-roadmap/archive/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md`
|
||||
- 요약: NomadCode workspace 실행 계약이 닫힌 뒤 provider별 health/model probe 차이를 Node adapter 내부로 숨기고, Edge가 공통으로 볼 수 있는 최소 상태와 capacity/in-flight/queued snapshot, FIFO admission queue 기준선을 만든다.
|
||||
|
||||
- [계획] Lemonade provider 서빙 경로 추가
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Edge OpenAI-compatible 입력 표면에서 Lemonade target/model의 모델 조
|
|||
|
||||
- 상태: 잠금
|
||||
- 결정 필요: 아래 체크리스트
|
||||
- [ ] Lemonade provider를 독립 `lemonade` adapter로 둘지, OpenAI-compatible inference server 공통 adapter 계열로 둘지 결정한다.
|
||||
- [x] Lemonade provider를 독립 `lemonade` adapter로 둘지, OpenAI-compatible inference server 공통 adapter 계열로 둘지 결정한다.
|
||||
- [ ] 실테스트에 사용할 Lemonade endpoint, served model name 또는 기준 model, 인증/헤더 필요 여부, streaming 지원 기대 동작을 확인한다.
|
||||
|
||||
## 범위
|
||||
|
|
@ -39,7 +39,7 @@ Edge OpenAI-compatible 입력 표면에서 Lemonade target/model의 모델 조
|
|||
|
||||
### Epic: [lemonade-provider] Lemonade Provider Serving Path
|
||||
|
||||
- [ ] [provider-boundary] Lemonade provider를 독립 adapter로 둘지 OpenAI-compatible inference server 공통 adapter로 둘지 결정 근거가 정리되어 있다.
|
||||
- [x] [provider-boundary] Lemonade provider를 독립 adapter로 둘지 OpenAI-compatible inference server 공통 adapter로 둘지 결정 근거가 정리되어 있다.
|
||||
- [ ] [config-contract] Lemonade endpoint, served model name 또는 model, auth/header, timeout, option passthrough 설정 계약이 정리되어 있다.
|
||||
- [ ] [availability] Lemonade provider의 endpoint/model probe 결과가 Node 공통 상태 모델 `unknown`, `available`, `unavailable`로 노출되는 기준이 검증되어 있다.
|
||||
- [ ] [models-chat] Edge OpenAI-compatible `/v1/models`와 non-streaming `/v1/chat/completions`가 Lemonade provider로 수렴하는 기준이 검증되어 있다.
|
||||
|
|
@ -72,6 +72,7 @@ Edge OpenAI-compatible 입력 표면에서 Lemonade target/model의 모델 조
|
|||
- 표준선(선택): 내부 실행 계약은 `adapter + target`을 유지하고, OpenAI-compatible API는 외부 호환 입력 표면으로 둔다.
|
||||
- 표준선(선택): Node provider 상태와 capacity queue 기반을 Lemonade provider에도 동일하게 적용한다.
|
||||
- 표준선(선택): 실제 provider가 보장하지 않는 확장 상태는 추가하지 않고, 초기 상태는 `unknown`, `available`, `unavailable`만 사용한다.
|
||||
- 표준선(확정): Lemonade는 독립 `lemonade` adapter를 새로 만들기보다 OpenAI-compatible inference server 공통 adapter 계열로 둔다. 공식 Lemonade Server 문서가 `/v1/models`, `/v1/chat/completions`와 streaming SSE를 OpenAI-compatible 표면으로 제공하고, 현재 코드에도 `vllm` adapter가 같은 `/v1/models`/`/v1/chat/completions` 기준선을 이미 구현하고 있기 때문이다.
|
||||
- 선행 작업: OpenAI Workspace Agent Execution Contract, Node provider 상태와 Capacity Queue 기반
|
||||
- 후속 작업: Lemonade provider 실테스트에서 확인된 serving 경로 안정화 보완
|
||||
- 확인 필요: Lemonade adapter 경계, 실테스트 endpoint/served model name 또는 model, 인증/헤더 필요 여부, streaming 기대 동작
|
||||
- 확인 필요: 실테스트 endpoint/served model name 또는 model, 인증/헤더 필요 여부, streaming 기대 동작
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
<!-- task=m-lemonade-provider-serving-validation/01_config_contract plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[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-15
|
||||
task=m-lemonade-provider-serving-validation/01_config_contract, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- Task ids:
|
||||
- `config-contract`: Lemonade endpoint, served model name 또는 model, auth/header, timeout, option passthrough 설정 계약이 정리되어 있다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G05.md` -> `code_review_cloud_G05_N.log`, `PLAN-cloud-G05.md` -> `plan_cloud_G05_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-lemonade-provider-serving-validation/01_config_contract/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] OpenAI-compatible config contract | [ ] |
|
||||
| [API-2] Typed proto payload and mapper | [ ] |
|
||||
| [API-3] Edge validation and example config | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `packages/go/config`에 OpenAI-compatible inference server 설정 계약을 추가하고 normalization/validation 테스트를 작성한다.
|
||||
- [ ] `proto/iop/runtime.proto`에 typed `OpenAICompatAdapterConfig`를 추가하고 `make proto`로 Go 생성물을 갱신한다.
|
||||
- [ ] Edge config payload mapper와 config validation, `configs/edge.yaml` 예시를 새 계약에 맞춘다.
|
||||
- [ ] 계획의 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 review stub에 붙인다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G05_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G05_M.log`로 아카이브한다.
|
||||
- [ ] `.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-lemonade-provider-serving-validation/01_config_contract/`를 `agent-task/archive/YYYY/MM/m-lemonade-provider-serving-validation/01_config_contract/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-lemonade-provider-serving-validation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G05.md`와 `CODE_REVIEW-cloud-G05.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로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- 새 `openai_compat` config가 legacy single config와 instance slice 모두에서 idempotent normalize를 보장하는지 확인한다.
|
||||
- proto generated 파일이 `make proto` 결과인지, 직접 편집 흔적이 없는지 확인한다.
|
||||
- Edge payload가 provider, endpoint, headers, queue fields를 typed oneof로 모두 전달하는지 확인한다.
|
||||
- `configs/edge.yaml`에 secret 값이나 개인 endpoint 원문이 들어가지 않았는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ make proto
|
||||
(output)
|
||||
$ go test -count=1 ./apps/edge/internal/node ./proto/gen/...
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-3 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/edgecmd
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ make proto
|
||||
(output)
|
||||
$ go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/edgecmd ./proto/gen/...
|
||||
(output)
|
||||
$ git diff --check
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[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.
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
<!-- task=m-lemonade-provider-serving-validation/01_config_contract plan=0 tag=API -->
|
||||
|
||||
# Plan - API
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
구현 완료 전 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 설계 결정, 검증 출력으로 반드시 채운다. 구현 중 사용자 결정, 사용자 소유 외부 환경, secret, 서비스 준비, 또는 범위 충돌 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 증거를 남기고 멈춘다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다. 최종 판정, archive, `complete.log` 작성은 code-review-skill 전용이다.
|
||||
|
||||
## 배경
|
||||
|
||||
현 마일스톤의 Lemonade provider는 OpenAI-compatible inference server 공통 adapter 계열로 두기로 정리됐다. 현재 config/proto 계약은 `ollama`와 `vllm` 중심이라 Lemonade endpoint, served model, header/auth, timeout, option passthrough의 설정 표면을 안정적으로 표현할 수 없다. 이 plan은 런타임 실행 구현 전에 Edge config에서 Node typed payload까지 이어지는 계약만 확정한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 차단이 생기면 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. 구현 에이전트가 채팅으로 직접 사용자에게 묻는 것은 금지되며, code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- Task ids:
|
||||
- `config-contract`: Lemonade endpoint, served model name 또는 model, auth/header, timeout, option passthrough 설정 계약이 정리되어 있다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-ops/skills/common/update-roadmap/SKILL.md`
|
||||
- `agent-ops/rules/project/domain/node/rules.md`
|
||||
- `agent-ops/rules/project/domain/edge/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/node-smoke.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- `proto/iop/node.proto`
|
||||
- `proto/iop/runtime.proto`
|
||||
- `packages/go/config/config.go`
|
||||
- `packages/go/config/config_test.go`
|
||||
- `apps/edge/internal/node/mapper.go`
|
||||
- `apps/edge/internal/node/mapper_test.go`
|
||||
- `apps/edge/internal/node/store.go`
|
||||
- `apps/edge/internal/node/store_test.go`
|
||||
- `apps/edge/internal/edgecmd/config.go`
|
||||
- `apps/edge/internal/edgecmd/edgecmd_test.go`
|
||||
- `configs/edge.yaml`
|
||||
- `configs/node.yaml`
|
||||
- `Makefile`
|
||||
- `apps/node/internal/adapters/factory.go`
|
||||
- `apps/node/internal/adapters/factory_internal_test.go`
|
||||
- `apps/node/internal/adapters/adapters_blackbox_test.go`
|
||||
- `apps/node/internal/adapters/vllm/vllm.go`
|
||||
- `apps/node/internal/adapters/vllm/vllm_test.go`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`.
|
||||
- `agent-test/local/rules.md`를 읽었고, 도메인 매핑에 따라 `node-smoke`, `edge-smoke`, `platform-common-smoke`를 읽었다.
|
||||
- 적용 명령: protobuf 변경은 `make proto`, 공통 설정/proto는 `go test ./packages/go/... ./proto/gen/...`, Edge config/mapper는 변경 패키지 `go test ./apps/edge/...` 중 관련 패키지로 축소한다.
|
||||
- `go`, `make`, `protoc`, `protoc-gen-go`는 현재 checkout에서 발견됐다.
|
||||
- 신선한 실행이 필요하므로 Go test cache 출력은 허용하지 않고 `-count=1`을 사용한다.
|
||||
- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `openai_compat` 설정 구조체, normalization, 중복 이름, queue validation은 기존 테스트가 없다. `packages/go/config/config_test.go`에 추가한다.
|
||||
- `OpenAICompatAdapterConfig` typed proto payload와 Edge mapper는 기존 `vllm` oneof 테스트만 있다. `apps/edge/internal/node/mapper_test.go`와 필요 시 `store_test.go`에 추가한다.
|
||||
- Edge config validation은 `vllm_instances` endpoint 검사만 있다. `apps/edge/internal/edgecmd/edgecmd_test.go`에 enabled `openai_compat_instances` empty endpoint rejection을 추가한다.
|
||||
- 실제 Lemonade execution, streaming, field smoke는 이 plan 범위 밖이다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- rename/remove 없음.
|
||||
- 추가되는 새 심볼의 call site는 `packages/go/config/config.go`, `apps/edge/internal/node/mapper.go`, `apps/edge/internal/edgecmd/config.go`, generated `proto/gen/iop/runtime.pb.go`, 관련 테스트로 제한한다.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 plan 작성 전에 평가했다.
|
||||
- 공유 task group: `agent-task/m-lemonade-provider-serving-validation`.
|
||||
- `01_config_contract`: 독립 선행 작업. 설정/proto/Edge payload 계약만 다룬다.
|
||||
- `02+01_openai_compatible_adapter`: `01`의 typed config와 generated proto가 PASS된 뒤 Node adapter를 구현한다.
|
||||
- `03+02_field_smoke`: `02`의 adapter가 PASS된 뒤 fake/real Lemonade smoke를 추가하고 실행한다.
|
||||
- 이 subtask는 predecessor가 없다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Node adapter 실행, SSE parsing, real Lemonade endpoint smoke는 제외한다. 계약이 먼저 고정되어야 후속 구현이 안정적이다.
|
||||
- `apps/node/internal/adapters/vllm`의 behavior 변경은 제외한다. 기존 vLLM 경로 회귀를 피하고 새 common adapter는 후속 subtask에서 구현한다.
|
||||
- host setup template, tracked `docs/`, archive roadmap/task 경로는 수정하지 않는다.
|
||||
- `configs/edge.yaml`에는 secret 값 없이 주석 예시만 추가한다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `cloud-G05`: protobuf/config schema 변경이고 Edge->Node payload 계약까지 닿지만, 범위와 테스트가 명확하게 제한된다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `packages/go/config`에 OpenAI-compatible inference server 설정 계약을 추가하고 normalization/validation 테스트를 작성한다.
|
||||
- [ ] `proto/iop/runtime.proto`에 typed `OpenAICompatAdapterConfig`를 추가하고 `make proto`로 Go 생성물을 갱신한다.
|
||||
- [ ] Edge config payload mapper와 config validation, `configs/edge.yaml` 예시를 새 계약에 맞춘다.
|
||||
- [ ] 계획의 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 review stub에 붙인다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## API-1 OpenAI-compatible config contract
|
||||
|
||||
### 문제
|
||||
|
||||
`packages/go/config/config.go:182-195`는 provider instance slice가 `ollama_instances`와 `vllm_instances`뿐이다. Lemonade를 common OpenAI-compatible adapter 계열로 두려면 endpoint, provider label, auth/header, timeout, queue 설정을 표현할 새 계약이 필요하다.
|
||||
|
||||
```go
|
||||
182 type AdaptersConf struct {
|
||||
183 // Legacy single-instance fields. When non-empty they are normalised into
|
||||
184 // OllamaInstances / VllmInstances during config load so that all downstream
|
||||
185 // code only needs to inspect the slice fields.
|
||||
186 Ollama OllamaConf `mapstructure:"ollama" yaml:"ollama"`
|
||||
187 Vllm VllmConf `mapstructure:"vllm" yaml:"vllm"`
|
||||
188 CLI CLIConf `mapstructure:"cli" yaml:"cli"`
|
||||
189
|
||||
190 // Multi-instance collections. Each entry carries a unique Name that acts as
|
||||
191 // the stable adapter instance identity within the node. Names must be unique
|
||||
192 // within each type collection; duplicate names are rejected at load time.
|
||||
193 OllamaInstances []OllamaInstanceConf `mapstructure:"ollama_instances" yaml:"ollama_instances,omitempty"`
|
||||
194 VllmInstances []VllmInstanceConf `mapstructure:"vllm_instances" yaml:"vllm_instances,omitempty"`
|
||||
195 }
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `AdaptersConf`에 legacy single config `OpenAICompat OpenAICompatConf`와 multi config `OpenAICompatInstances []OpenAICompatInstanceConf`를 추가한다.
|
||||
- YAML key는 `openai_compat`와 `openai_compat_instances`, adapter type string은 `openai_compat`로 고정한다.
|
||||
- `OpenAICompatConf`와 `OpenAICompatInstanceConf` 필드는 `enabled`, `provider`, `endpoint`, `headers map[string]string`, `capacity`, `max_queue`, `queue_timeout_ms`, `request_timeout_ms`로 둔다.
|
||||
- `provider`는 비어 있으면 후속 adapter에서 `"openai_compat"`로 취급할 수 있게 보존하고, Lemonade 예시는 `"lemonade"`를 사용한다.
|
||||
- legacy `openai_compat`가 enabled면 name `"openai_compat"` instance로 normalize한다. 명시 instance와 충돌하면 `provider/endpoint/headers/capacity/max_queue/queue_timeout_ms/request_timeout_ms` 불일치 에러를 낸다.
|
||||
- enabled `openai_compat_instances`도 `validateProviderQueueConfig`를 통과해야 한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `packages/go/config/config.go`: struct, normalize, `sameOpenAICompatInstance`, `findOpenAICompatInstance`, queue validation을 추가한다.
|
||||
- [ ] `packages/go/config/config_test.go`: load/normalize/idempotent/conflict/duplicate/negative queue 테스트를 추가한다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- `TestLoadEdge_OpenAICompatInstances`
|
||||
- `TestNormalizeAdapters_OpenAICompatIdempotent`
|
||||
- `TestNormalizeAdapters_OpenAICompatConflictErrors`
|
||||
- `TestNormalizeAdapters_OpenAICompatDuplicateNameRejects`
|
||||
- `TestNormalizeAdapters_OpenAICompatQueueValidation`
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./packages/go/config
|
||||
```
|
||||
|
||||
기대 결과: 새 config 계약 테스트와 기존 config 테스트가 모두 PASS.
|
||||
|
||||
## API-2 Typed proto payload and mapper
|
||||
|
||||
### 문제
|
||||
|
||||
`proto/iop/runtime.proto:166-178`의 typed oneof는 `cli`, `ollama`, `vllm`, `mock`뿐이다. `apps/edge/internal/node/mapper.go:58-81`도 vLLM instance만 typed `VllmAdapterConfig`로 보낸다.
|
||||
|
||||
```proto
|
||||
166 message AdapterConfig {
|
||||
167 string type = 1; // "mock" | "ollama" | "vllm" | "cli"
|
||||
168 bool enabled = 2;
|
||||
169 google.protobuf.Struct settings = 3; // legacy/compat path; new adapters use oneof
|
||||
170 oneof config {
|
||||
171 CLIAdapterConfig cli = 4;
|
||||
172 OllamaAdapterConfig ollama = 5;
|
||||
173 VllmAdapterConfig vllm = 6;
|
||||
174 MockAdapterConfig mock = 7;
|
||||
175 }
|
||||
176 string name = 8; // stable instance identity; empty = legacy single-instance
|
||||
177 string target = 9; // optional default target/route hint for this instance
|
||||
178 }
|
||||
```
|
||||
|
||||
```go
|
||||
58 for _, inst := range rec.Adapters.VllmInstances {
|
||||
59 if !inst.Enabled {
|
||||
60 continue
|
||||
61 }
|
||||
62 key := "vllm:" + inst.Name
|
||||
63 if _, dup := seen[key]; dup {
|
||||
64 return nil, fmt.Errorf("duplicate vllm instance name %q", inst.Name)
|
||||
65 }
|
||||
66 seen[key] = struct{}{}
|
||||
67 payload.Adapters = append(payload.Adapters, &iop.AdapterConfig{
|
||||
68 Type: "vllm",
|
||||
69 Enabled: true,
|
||||
70 Name: inst.Name,
|
||||
71 Config: &iop.AdapterConfig_Vllm{
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `runtime.proto`에 `OpenAICompatAdapterConfig openai_compat = 10;`을 oneof에 추가한다. 기존 tag는 재사용하지 않는다.
|
||||
- `OpenAICompatAdapterConfig` message를 추가하고 fields는 `provider=1`, `endpoint=2`, `map<string,string> headers=3`, `capacity=4`, `max_queue=5`, `queue_timeout_ms=6`, `request_timeout_ms=7`로 둔다.
|
||||
- comment의 type 목록에 `"openai_compat"`를 추가한다.
|
||||
- `make proto`로 `proto/gen/iop/runtime.pb.go`를 갱신한다. generated 파일은 직접 수정하지 않는다.
|
||||
- `BuildConfigPayload`가 enabled `OpenAICompatInstances`를 `Type: "openai_compat"`, `Name: inst.Name`, typed oneof로 보낸다.
|
||||
- duplicate key prefix는 `"openai_compat:"`를 사용한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `proto/iop/runtime.proto`: oneof와 message 추가.
|
||||
- [ ] `proto/gen/iop/runtime.pb.go`: `make proto` 결과로만 갱신.
|
||||
- [ ] `apps/edge/internal/node/mapper.go`: OpenAI-compatible instance mapping 추가.
|
||||
- [ ] `apps/edge/internal/node/mapper_test.go`: payload type/name/provider/endpoint/headers/queue 필드 검증 추가.
|
||||
- [ ] `apps/edge/internal/node/store_test.go`: `LoadFromConfig` normalize 결과가 payload까지 유지되는지 필요 시 추가.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- `TestBuildConfigPayload_OpenAICompatInstances`
|
||||
- `TestLoadFromConfig_OpenAICompatInPayload` 또는 mapper test로 충분하면 store test는 skip 사유를 review에 기록한다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
make proto
|
||||
go test -count=1 ./apps/edge/internal/node ./proto/gen/...
|
||||
```
|
||||
|
||||
기대 결과: generated proto compile과 Edge payload tests PASS.
|
||||
|
||||
## API-3 Edge validation and example config
|
||||
|
||||
### 문제
|
||||
|
||||
`apps/edge/internal/edgecmd/config.go:167-187`은 enabled adapter 판정과 endpoint validation에서 Ollama, CLI, vLLM만 본다. `configs/edge.yaml:49-76`, `configs/edge.yaml:84-189`에도 Lemonade/OpenAI-compatible route와 node adapter 예시가 없다.
|
||||
|
||||
```go
|
||||
167 hasOllama := anyOllamaEnabled(n.Adapters)
|
||||
168 hasVllm := anyVllmEnabled(n.Adapters)
|
||||
169 if !hasOllama && !n.Adapters.CLI.Enabled && !hasVllm {
|
||||
170 return fmt.Errorf("node %q: at least one adapter must be enabled", name)
|
||||
171 }
|
||||
```
|
||||
|
||||
```yaml
|
||||
68 # - model: "qwen3"
|
||||
69 # adapter: "vllm"
|
||||
70 # target: "qwen3-72b"
|
||||
71 # node: "node-gpu-01"
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `anyOpenAICompatEnabled` helper를 추가하고 enabled adapter validation에 포함한다.
|
||||
- enabled `openai_compat` 또는 `openai_compat_instances`의 empty endpoint를 reject한다.
|
||||
- `configs/edge.yaml`에 commented route 예시를 추가한다: `adapter: "openai_compat"`, `target: "<lemonade-served-model>"`, `node: "node-lemonade-01"`.
|
||||
- node adapter 예시에도 commented `openai_compat_instances`를 추가한다. endpoint 예시는 Lemonade 기본 local API인 `http://127.0.0.1:13305`를 comment로 두고, headers는 secret 값 없이 placeholder comment만 둔다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/edgecmd/config.go`: helper와 validation 추가.
|
||||
- [ ] `apps/edge/internal/edgecmd/edgecmd_test.go`: enabled OpenAI-compatible instance, empty endpoint rejection, adapter-only node pass 테스트 추가.
|
||||
- [ ] `configs/edge.yaml`: commented example 추가. secret 값 금지.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- `TestValidateEdgeConfig_OpenAICompatInstanceEnabled`
|
||||
- `TestValidateEdgeConfig_OpenAICompatInstanceEmptyEndpointRejected`
|
||||
- legacy single config를 추가했다면 `TestValidateEdgeConfig_OpenAICompatLegacyEmptyEndpointRejected`
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/edgecmd
|
||||
```
|
||||
|
||||
기대 결과: config validation tests PASS.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `packages/go/config/config.go` | API-1 |
|
||||
| `packages/go/config/config_test.go` | API-1 |
|
||||
| `proto/iop/runtime.proto` | API-2 |
|
||||
| `proto/gen/iop/runtime.pb.go` | API-2 |
|
||||
| `apps/edge/internal/node/mapper.go` | API-2 |
|
||||
| `apps/edge/internal/node/mapper_test.go` | API-2 |
|
||||
| `apps/edge/internal/node/store_test.go` | API-2 |
|
||||
| `apps/edge/internal/edgecmd/config.go` | API-3 |
|
||||
| `apps/edge/internal/edgecmd/edgecmd_test.go` | API-3 |
|
||||
| `configs/edge.yaml` | API-3 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
make proto
|
||||
go test -count=1 ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/edgecmd ./proto/gen/...
|
||||
git diff --check
|
||||
```
|
||||
|
||||
기대 결과: 모든 명령이 exit 0. `git diff --check`는 whitespace error 없음.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<!-- task=m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[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-15
|
||||
task=m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- Task ids:
|
||||
- `availability`: Lemonade provider의 endpoint/model probe 결과가 Node 공통 상태 모델 `unknown`, `available`, `unavailable`로 노출되는 기준이 검증되어 있다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-local-G08.md` -> `code_review_local_G08_N.log`, `PLAN-local-G08.md` -> `plan_local_G08_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Common OpenAI-compatible adapter | [ ] |
|
||||
| [API-2] Factory registration | [ ] |
|
||||
| [API-3] Node execution regression coverage | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] 구현 시작 전 `01_config_contract`의 `complete.log`를 확인하고, 선행 계약의 type/field 이름을 이 plan에 맞춰 반영한다.
|
||||
- [ ] `openai_compat` Node adapter를 추가하고 `/v1/models`, `/v1/chat/completions` streaming, headers, option passthrough를 구현한다.
|
||||
- [ ] factory가 typed/legacy `openai_compat` payload를 registry에 등록하도록 연결한다.
|
||||
- [ ] Node adapter/factory/blackbox 테스트를 작성하고 기존 vLLM 테스트가 그대로 PASS하는지 확인한다.
|
||||
- [ ] 계획의 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 review stub에 붙인다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G08_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_local_G08_M.log`로 아카이브한다.
|
||||
- [ ] `.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-lemonade-provider-serving-validation/02+01_openai_compatible_adapter/`를 `agent-task/archive/YYYY/MM/m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-lemonade-provider-serving-validation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G08.md`와 `CODE_REVIEW-local-G08.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로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `01_config_contract` predecessor completion이 실제 `complete.log`로 충족됐는지 확인한다.
|
||||
- `openai_compat` adapter가 `vllm` adapter name/behavior를 깨뜨리지 않았는지 확인한다.
|
||||
- headers, options passthrough, `[DONE]`, finish_reason, usage fallback이 tests와 구현에서 일치하는지 확인한다.
|
||||
- endpoint가 root 또는 trailing `/v1` base URL이어도 provider request path가 중복되지 않는지 확인한다.
|
||||
- provider status가 `unknown`, `available`, `unavailable` baseline 밖으로 확장되지 않았는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/node/internal/adapters
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-3 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/node/internal/adapters
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm ./apps/node/internal/adapters ./apps/node/...
|
||||
(output)
|
||||
$ git diff --check
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[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.
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
<!-- task=m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter plan=0 tag=API -->
|
||||
|
||||
# Plan - API
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
구현 완료 전 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 설계 결정, 검증 출력으로 반드시 채운다. 구현 중 사용자 결정, 사용자 소유 외부 환경, secret, 서비스 준비, 또는 범위 충돌 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 증거를 남기고 멈춘다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다. 최종 판정, archive, `complete.log` 작성은 code-review-skill 전용이다.
|
||||
|
||||
## 배경
|
||||
|
||||
Lemonade Server는 OpenAI-compatible `/v1/models`와 `/v1/chat/completions` 표면을 제공한다. 현재 Node에는 vLLM 전용 이름의 adapter가 같은 HTTP/SSE 흐름을 일부 구현하지만, Lemonade를 vLLM adapter로 노출하면 provider 경계와 설정 계약이 흐려진다. 이 plan은 선행 config/proto 계약을 사용해 `openai_compat` Node adapter를 구현하고 provider availability 기준을 검증한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 차단이 생기면 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. 구현 에이전트가 채팅으로 직접 사용자에게 묻는 것은 금지되며, code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- Task ids:
|
||||
- `availability`: Lemonade provider의 endpoint/model probe 결과가 Node 공통 상태 모델 `unknown`, `available`, `unavailable`로 노출되는 기준이 검증되어 있다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-ops/rules/project/domain/node/rules.md`
|
||||
- `agent-ops/rules/project/domain/edge/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/node-smoke.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- `proto/iop/runtime.proto`
|
||||
- `packages/go/config/config.go`
|
||||
- `apps/node/internal/runtime/types.go`
|
||||
- `apps/node/internal/adapters/factory.go`
|
||||
- `apps/node/internal/adapters/factory_internal_test.go`
|
||||
- `apps/node/internal/adapters/adapters_blackbox_test.go`
|
||||
- `apps/node/internal/adapters/vllm/vllm.go`
|
||||
- `apps/node/internal/adapters/vllm/vllm_test.go`
|
||||
- `apps/edge/internal/openai/types.go`
|
||||
- `apps/edge/internal/openai/chat_handler.go`
|
||||
- `apps/edge/internal/openai/server_test.go`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`.
|
||||
- `agent-test/local/rules.md`, `node-smoke`, `edge-smoke`, `platform-common-smoke`를 읽었다.
|
||||
- 적용 명령: Node adapter 변경은 변경 패키지 또는 `go test ./apps/node/...`; OpenAI-compatible 경계는 후속 smoke에서 `iop-edge smoke openai` 또는 동등한 HTTP 확인을 적용한다.
|
||||
- 이 subtask는 httptest 기반 deterministic Node adapter 검증까지 담당한다. Edge HTTP field convergence는 `03+02_field_smoke`에서 담당한다.
|
||||
- 신선한 실행이 필요하므로 Go test cache 출력은 허용하지 않고 `-count=1`을 사용한다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `apps/node/internal/adapters/vllm/vllm_test.go`는 `/v1/models`, SSE delta, target miss를 검증하지만 adapter name, provider label, headers, option passthrough가 없다.
|
||||
- `apps/node/internal/adapters/factory_internal_test.go`는 vLLM typed/legacy config만 검증한다. `openai_compat` typed config와 legacy settings fallback 테스트가 필요하다.
|
||||
- `apps/node/internal/adapters/adapters_blackbox_test.go`는 vLLM execution만 있다. `openai_compat` registry key, headers, options, complete metadata를 검증하는 blackbox test가 필요하다.
|
||||
- real Lemonade endpoint 검증은 사용자 소유 환경에 의존하므로 이 subtask 범위가 아니다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- rename/remove 없음.
|
||||
- 새 adapter type string `openai_compat` call site: `apps/node/internal/adapters/factory.go`, `apps/node/internal/adapters/adapters_blackbox_test.go`, 후속 `apps/edge/internal/node/mapper.go` payload 출력.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 plan 작성 전에 평가했다.
|
||||
- 공유 task group: `agent-task/m-lemonade-provider-serving-validation`.
|
||||
- `01_config_contract`: 선행 config/proto 계약. 현재 active/archive `complete.log` 없음.
|
||||
- `02+01_openai_compatible_adapter`: 이 plan. predecessor `01`은 아직 만족되지 않았다. 구현 시작 전 같은 task group의 `01_*` active/archive `complete.log`가 있어야 한다.
|
||||
- `03+02_field_smoke`: 후속 field smoke. 이 plan의 PASS 후 시작한다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 선행 `01_config_contract`의 type/field 이름을 사용한다. 선행 결과와 이름이 다르면 이 plan의 `계획 대비 변경 사항`에 맞춘다.
|
||||
- `apps/node/internal/adapters/vllm`의 public behavior를 깨뜨리지 않는다. 공통 로직 추출은 허용하지만 기존 `vllm` adapter name, default capacity, tests는 유지한다.
|
||||
- Edge OpenAI HTTP handler의 request parsing/response formatting은 수정하지 않는다. Edge route convergence는 후속 smoke에서 검증한다.
|
||||
- real Lemonade installation, credentials, external endpoint 준비는 제외한다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G08`: streaming/probe/error handling을 다루지만 httptest로 deterministic하게 검증 가능하고 Node adapter 경계로 범위를 제한한다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] 구현 시작 전 `01_config_contract`의 `complete.log`를 확인하고, 선행 계약의 type/field 이름을 이 plan에 맞춰 반영한다.
|
||||
- [ ] `openai_compat` Node adapter를 추가하고 `/v1/models`, `/v1/chat/completions` streaming, headers, option passthrough를 구현한다.
|
||||
- [ ] factory가 typed/legacy `openai_compat` payload를 registry에 등록하도록 연결한다.
|
||||
- [ ] Node adapter/factory/blackbox 테스트를 작성하고 기존 vLLM 테스트가 그대로 PASS하는지 확인한다.
|
||||
- [ ] 계획의 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 review stub에 붙인다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- 디렉터리명 `02+01_openai_compatible_adapter`의 predecessor는 `01`뿐이다.
|
||||
- 구현 시작 전 `agent-task/m-lemonade-provider-serving-validation/01_config_contract/complete.log` 또는 archive의 matching `01_*` `complete.log`를 확인한다.
|
||||
- 이 plan 본문은 추가 runtime dependency를 만들지 않는다.
|
||||
|
||||
## API-1 Common OpenAI-compatible adapter
|
||||
|
||||
### 문제
|
||||
|
||||
`apps/node/internal/adapters/vllm/vllm.go:1-3`은 이미 OpenAI-compatible inference engine이라고 설명하지만 package/type/name은 vLLM 전용이다. `apps/node/internal/adapters/vllm/vllm.go:85-123`은 request body가 `model/messages/stream`뿐이라 Lemonade의 option passthrough와 auth/header 계약을 사용할 수 없다.
|
||||
|
||||
```go
|
||||
1 // Package vllm provides an Adapter for OpenAI-compatible inference engines
|
||||
2 // such as vLLM and SGLang via the /v1/chat/completions SSE endpoint.
|
||||
3 package vllm
|
||||
```
|
||||
|
||||
```go
|
||||
109 chatReq := vllmChatRequest{
|
||||
110 Model: model,
|
||||
111 Messages: messages,
|
||||
112 Stream: true,
|
||||
113 }
|
||||
114 body, err := json.Marshal(chatReq)
|
||||
115 if err != nil {
|
||||
116 return fmt.Errorf("vllm adapter: marshal request: %w", err)
|
||||
117 }
|
||||
118 req, err := http.NewRequestWithContext(ctx, http.MethodPost, joinURL(v.endpoint, "/v1/chat/completions"), bytes.NewReader(body))
|
||||
119 if err != nil {
|
||||
120 return fmt.Errorf("vllm adapter: build request: %w", err)
|
||||
121 }
|
||||
122 req.Header.Set("Content-Type", "application/json")
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- 새 package `apps/node/internal/adapters/openai_compat`를 만든다.
|
||||
- `const Name = "openai_compat"`와 adapter type을 정의한다.
|
||||
- config는 선행 task의 `config.OpenAICompatConf`를 사용한다.
|
||||
- endpoint는 `strings.TrimRight(endpoint, "/")`; empty endpoint는 execution/probe에서 error. `joinOpenAIPath` helper는 endpoint path가 이미 `/v1`로 끝나도 `/v1/v1/...`을 만들지 않아야 한다.
|
||||
- HTTP headers는 `/v1/models`와 `/v1/chat/completions` 양쪽에 적용한다. `Content-Type: application/json`은 POST에서 adapter가 항상 설정하고, user header가 같은 key를 제공하면 secret/auth header만 허용하되 content type override는 명시적으로 막거나 review에 근거를 기록한다.
|
||||
- request body는 `map[string]any`로 만들고 adapter-owned `model`, `messages`, `stream=true`를 우선한다. `spec.Input["options"].(map[string]any)`는 top-level OpenAI-compatible request field로 복사하되 `model`, `messages`, `stream`은 덮어쓰지 못하게 한다. `tools`, `format`, `think`, `keep_alive`는 현재 Edge input 그대로 필요 시 복사한다.
|
||||
- SSE parser는 `data: [DONE]`에서 complete를 emit한다. chunk `choices[0].delta.content`는 `EventTypeDelta`, `choices[0].delta.reasoning_content`가 있으면 `EventTypeReasoningDelta`로 emit한다.
|
||||
- chunk `choices[0].finish_reason`은 complete event `Metadata["finish_reason"]`로 보존한다. usage가 있으면 `RuntimeEvent.Usage`에 반영하고, 없으면 현재 vLLM처럼 output token fallback을 사용한다.
|
||||
- `Capabilities`와 `ProbeProvider`는 `/v1/models`의 `data[].id`를 사용하고 status는 `runtime.NormalizeProviderStatus`로 `available`/`unavailable`만 반환한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/node/internal/adapters/openai_compat/openai_compat.go`: adapter 구현 추가.
|
||||
- [ ] `apps/node/internal/adapters/openai_compat/openai_compat_test.go`: capabilities/probe/execute/header/options/stream tests 추가.
|
||||
- [ ] 필요 시 `apps/node/internal/adapters/vllm/vllm.go`: 공통 helper reuse를 위한 최소 수정. vLLM behavior/name 회귀 금지.
|
||||
- [ ] 필요 시 `apps/node/internal/adapters/vllm/vllm_test.go`: 기존 expectations 유지 또는 공통 helper 이동에 따른 import 조정.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- `TestOpenAICompatCapabilitiesQueryModels`
|
||||
- `TestOpenAICompatProbeProviderAvailability`
|
||||
- `TestOpenAICompatExecuteStreamsDeltasAndFinishReason`
|
||||
- `TestOpenAICompatExecuteSendsHeaders`
|
||||
- `TestOpenAICompatExecutePassesOptionsAsTopLevelFields`
|
||||
- `TestOpenAICompatExecuteRejectsEmptyEndpointOrModel`
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm
|
||||
```
|
||||
|
||||
기대 결과: 새 adapter tests PASS, 기존 vLLM tests PASS.
|
||||
|
||||
## API-2 Factory registration
|
||||
|
||||
### 문제
|
||||
|
||||
`apps/node/internal/adapters/factory.go:27-56`는 `mock`, `ollama`, `vllm`, `cli`만 등록한다. 선행 task가 `openai_compat` typed payload를 만들더라도 Node가 이를 모르면 등록 실패가 난다.
|
||||
|
||||
```go
|
||||
27 switch typeName {
|
||||
28 case "mock":
|
||||
29 reg.RegisterKeyed(instanceKey, typeName, mock.New(logger))
|
||||
30 case "ollama":
|
||||
31 var cfg config.OllamaConf
|
||||
32 if m := ac.GetOllama(); m != nil {
|
||||
33 cfg = ollamaConfFromProto(m)
|
||||
34 } else {
|
||||
35 cfg = ollamaConfFromStruct(ac.GetSettings())
|
||||
36 }
|
||||
37 reg.RegisterKeyed(instanceKey, typeName, ollama.New(cfg, logger, instanceKey))
|
||||
38 case "vllm":
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- factory imports에 `iop/apps/node/internal/adapters/openai_compat`를 추가한다.
|
||||
- switch에 `case "openai_compat"`를 추가한다.
|
||||
- typed payload는 `ac.GetOpenaiCompat()` 또는 generated accessor 이름을 사용한다. accessor 이름은 `make proto` 결과를 확인해 정확히 맞춘다.
|
||||
- `openAICompatConfFromProto`와 `openAICompatConfFromStruct` helper를 추가한다.
|
||||
- legacy settings fallback은 `provider`, `endpoint`, `headers`, queue fields를 읽는다.
|
||||
- `reg.RegisterKeyed(instanceKey, typeName, openai_compat.New(cfg, logger, instanceKey))` 형태로 등록한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/node/internal/adapters/factory.go`: import, switch case, proto/struct config helpers 추가.
|
||||
- [ ] `apps/node/internal/adapters/factory_internal_test.go`: typed config와 legacy settings fallback 테스트 추가.
|
||||
- [ ] `apps/node/internal/adapters/adapters_blackbox_test.go`: payload에서 registry key와 execution까지 blackbox 테스트 추가.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- `TestOpenAICompatConfFromProto`
|
||||
- `TestOpenAICompatConfFromStruct_LegacyPayload`
|
||||
- `TestBuildFromPayload_OpenAICompatTypedRegistered`
|
||||
- `TestBuildFromPayload_OpenAICompatExecution`
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/node/internal/adapters
|
||||
```
|
||||
|
||||
기대 결과: factory/internal blackbox tests PASS.
|
||||
|
||||
## API-3 Node execution regression coverage
|
||||
|
||||
### 문제
|
||||
|
||||
`apps/node/internal/adapters/adapters_blackbox_test.go:520-585`는 vLLM execution만 검증한다. `openai_compat`의 registry key, headers, options passthrough, model fallback이 route-level로 깨져도 현재 blackbox coverage가 없다.
|
||||
|
||||
```go
|
||||
520 func TestBuildFromPayload_VllmExecution(t *testing.T) {
|
||||
521 var gotModel string
|
||||
522 var gotStream bool
|
||||
...
|
||||
549 reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
||||
550 Adapters: []*iop.AdapterConfig{
|
||||
551 {
|
||||
552 Type: "vllm",
|
||||
553 Name: "vllm@gpu",
|
||||
554 Enabled: true,
|
||||
555 Config: &iop.AdapterConfig_Vllm{Vllm: &iop.VllmAdapterConfig{Endpoint: server.URL}},
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- blackbox test server가 `/v1/models`와 `/v1/chat/completions`를 모두 제공하게 한다.
|
||||
- typed `OpenAICompatAdapterConfig` payload로 registry를 만들고 `reg.Get("lemonade-local")` lookup을 검증한다.
|
||||
- `ExecutionSpec{Target:"lemonade-model", Input:{"messages":..., "options":{"temperature":0.2,"max_tokens":8}}}` 실행 후 server가 header와 top-level options를 받았는지 확인한다.
|
||||
- stream은 `data: {"choices":[{"delta":{"content":"LEMONADE_"}}]}`와 `[DONE]`을 사용한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/node/internal/adapters/adapters_blackbox_test.go`: integration-style blackbox test 추가.
|
||||
- [ ] 필요 시 shared test sink/helper를 중복 최소화한다. 과도한 test abstraction은 만들지 않는다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- `TestBuildFromPayload_OpenAICompatExecution`
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/node/internal/adapters
|
||||
```
|
||||
|
||||
기대 결과: registry/factory/execution blackbox tests PASS.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/node/internal/adapters/openai_compat/openai_compat.go` | API-1 |
|
||||
| `apps/node/internal/adapters/openai_compat/openai_compat_test.go` | API-1 |
|
||||
| `apps/node/internal/adapters/vllm/vllm.go` | API-1 |
|
||||
| `apps/node/internal/adapters/vllm/vllm_test.go` | API-1 |
|
||||
| `apps/node/internal/adapters/factory.go` | API-2 |
|
||||
| `apps/node/internal/adapters/factory_internal_test.go` | API-2 |
|
||||
| `apps/node/internal/adapters/adapters_blackbox_test.go` | API-2, API-3 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm ./apps/node/internal/adapters ./apps/node/...
|
||||
git diff --check
|
||||
```
|
||||
|
||||
기대 결과: 모든 명령이 exit 0. `git diff --check`는 whitespace error 없음.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<!-- task=m-lemonade-provider-serving-validation/03+02_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.**
|
||||
> 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-15
|
||||
task=m-lemonade-provider-serving-validation/03+02_field_smoke, plan=0, tag=TEST
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- Task ids:
|
||||
- `models-chat`: Edge OpenAI-compatible `/v1/models`와 non-streaming `/v1/chat/completions`가 Lemonade provider로 수렴하는 기준이 검증되어 있다.
|
||||
- `streaming`: streaming `/v1/chat/completions`에서 SSE chunk, finish reason, 종료 신호가 Lemonade provider 경로로 안정적으로 전달되는지 검증되어 있다.
|
||||
- `field-smoke`: split-host field smoke에서 Lemonade target/model을 선택해 모델 조회와 chat 왕복이 검증되어 있다.
|
||||
- `follow-up-scope`: 코드 수정이 필요한 항목은 이 Milestone의 추가 Task 또는 같은 Milestone task group의 후속 plan으로 정리되어 있다.
|
||||
- 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-lemonade-provider-serving-validation/03+02_field_smoke/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [TEST-1] Lemonade smoke script | [ ] |
|
||||
| [TEST-2] Makefile target and command contract | [ ] |
|
||||
| [TEST-3] Real Lemonade field gate and follow-up scope | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] 구현 시작 전 `02+01_openai_compatible_adapter`의 `complete.log`를 확인하고, 선행 adapter type/config 이름을 smoke config에 맞춘다.
|
||||
- [ ] `scripts/e2e-openai-lemonade.sh`를 추가해 fake mode와 real mode에서 Edge/Node/OpenAI-compatible route를 검증한다.
|
||||
- [ ] `Makefile`에 명시 실행 target `test-openai-lemonade`를 추가하되 `test-e2e` 기본 dependency에는 넣지 않는다.
|
||||
- [ ] fake mode smoke, real mode smoke, 관련 Go package tests를 실행한다. real mode 환경값이 없으면 review stub의 `사용자 리뷰 요청`에 기록하고 PASS를 주장하지 않는다.
|
||||
- [ ] smoke 결과 큰 코드 이슈가 발견되면 이 plan 범위를 넘겨 수정하지 말고 증거를 남겨 후속 plan으로 넘긴다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [ ] `.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-lemonade-provider-serving-validation/03+02_field_smoke/`를 `agent-task/archive/YYYY/MM/m-lemonade-provider-serving-validation/03+02_field_smoke/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-lemonade-provider-serving-validation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] 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로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `02+01_openai_compatible_adapter` predecessor completion이 실제 `complete.log`로 충족됐는지 확인한다.
|
||||
- fake mode가 Edge/Node/OpenAI-compatible route를 실제로 통과하고, real mode 없이는 PASS를 주장하지 않았는지 확인한다.
|
||||
- script가 auth header value나 개인 endpoint를 tracked 파일 또는 review 출력에 노출하지 않았는지 확인한다.
|
||||
- real smoke 실패가 큰 코드 이슈라면 후속 plan으로 넘어가고 이 task가 PASS되지 않았는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### TEST-1 중간 검증
|
||||
```bash
|
||||
$ bash -n scripts/e2e-openai-lemonade.sh
|
||||
(output)
|
||||
$ ./scripts/e2e-openai-lemonade.sh
|
||||
(output)
|
||||
```
|
||||
|
||||
### TEST-2 중간 검증
|
||||
```bash
|
||||
$ make test-openai-lemonade
|
||||
(output)
|
||||
```
|
||||
|
||||
### TEST-3 중간 검증
|
||||
```bash
|
||||
$ IOP_LEMONADE_MODE=real ./scripts/e2e-openai-lemonade.sh
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ bash -n scripts/e2e-openai-lemonade.sh
|
||||
(output)
|
||||
$ ./scripts/e2e-openai-lemonade.sh
|
||||
(output)
|
||||
$ make test-openai-lemonade
|
||||
(output)
|
||||
$ IOP_LEMONADE_MODE=real ./scripts/e2e-openai-lemonade.sh
|
||||
(output)
|
||||
$ go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/edgecmd ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters
|
||||
(output)
|
||||
$ git diff --check
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[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.
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
<!-- task=m-lemonade-provider-serving-validation/03+02_field_smoke plan=0 tag=TEST -->
|
||||
|
||||
# Plan - TEST
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
구현 완료 전 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 설계 결정, 검증 출력으로 반드시 채운다. 구현 중 사용자 결정, 사용자 소유 외부 환경, secret, 서비스 준비, 또는 범위 충돌 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 증거를 남기고 멈춘다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다. 최종 판정, archive, `complete.log` 작성은 code-review-skill 전용이다.
|
||||
|
||||
## 배경
|
||||
|
||||
Lemonade provider의 최종 기준은 Edge OpenAI-compatible surface에서 모델 조회, non-streaming chat, streaming chat이 provider 경로로 수렴하는지 확인하는 것이다. 현재 repo에는 Ollama 전용 OpenAI e2e script와 generic `iop-edge smoke openai`만 있고, Lemonade/OpenAI-compatible provider 경로를 선택해 검증하는 field smoke가 없다. 이 plan은 선행 adapter 구현을 전제로 fake mode와 real mode를 모두 지원하는 smoke를 추가한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 차단이 생기면 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. 구현 에이전트가 채팅으로 직접 사용자에게 묻는 것은 금지되며, code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. 실제 Lemonade endpoint/model/auth 값이 준비되지 않아 real mode smoke를 실행할 수 없으면 이 섹션에 기록하고 PASS를 주장하지 않는다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- Task ids:
|
||||
- `models-chat`: Edge OpenAI-compatible `/v1/models`와 non-streaming `/v1/chat/completions`가 Lemonade provider로 수렴하는 기준이 검증되어 있다.
|
||||
- `streaming`: streaming `/v1/chat/completions`에서 SSE chunk, finish reason, 종료 신호가 Lemonade provider 경로로 안정적으로 전달되는지 검증되어 있다.
|
||||
- `field-smoke`: split-host field smoke에서 Lemonade target/model을 선택해 모델 조회와 chat 왕복이 검증되어 있다.
|
||||
- `follow-up-scope`: 코드 수정이 필요한 항목은 이 Milestone의 추가 Task 또는 같은 Milestone task group의 후속 plan으로 정리되어 있다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-ops/rules/project/domain/node/rules.md`
|
||||
- `agent-ops/rules/project/domain/edge/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/node-smoke.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
|
||||
- `scripts/e2e-openai-ollama.sh`
|
||||
- `apps/edge/internal/edgecmd/smoke_openai.go`
|
||||
- `Makefile`
|
||||
- `configs/edge.yaml`
|
||||
- `configs/node.yaml`
|
||||
- `apps/edge/internal/openai/types.go`
|
||||
- `apps/edge/internal/openai/chat_handler.go`
|
||||
- `apps/edge/internal/openai/server_test.go`
|
||||
- `apps/node/internal/adapters/vllm/vllm.go`
|
||||
- `apps/node/internal/adapters/vllm/vllm_test.go`
|
||||
- `apps/node/internal/adapters/adapters_blackbox_test.go`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`.
|
||||
- `agent-test/local/rules.md`, `edge-smoke`, `node-smoke`, `platform-common-smoke`를 읽었다.
|
||||
- 적용 명령: Edge OpenAI-compatible 경계 변경은 `iop-edge smoke openai` 또는 동등한 `/healthz`, `/v1/models`, `/v1/responses` 확인이 필요하다. 이 plan은 추가로 `/v1/chat/completions` non-streaming/streaming curl 검증을 포함한다.
|
||||
- field runtime이나 외부 provider evidence는 원격 runner 또는 사용자 소유 host가 필요할 수 있다. 이 plan의 fake mode는 local deterministic check이고, Roadmap `field-smoke` PASS에는 real mode evidence가 필요하다.
|
||||
- secret, token, 개인 endpoint 원문은 tracked 파일과 최종 보고에 기록하지 않는다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `scripts/e2e-openai-ollama.sh`는 Ollama fake server와 Ollama adapter만 검증한다. Lemonade/OpenAI-compatible adapter route는 없다.
|
||||
- `apps/edge/internal/edgecmd/smoke_openai.go`는 `/healthz`, `/v1/models`, `/v1/responses`만 확인한다. `/v1/chat/completions` non-streaming/streaming 검증이 없다.
|
||||
- real Lemonade endpoint/model/auth/streaming expectation은 아직 마일스톤 잠금에 남아 있으므로, 구현 시 environment가 없으면 사용자 리뷰 요청으로 멈춰야 한다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- rename/remove 없음.
|
||||
- 새 script target `test-openai-lemonade` call site는 `Makefile`에 한정한다.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 plan 작성 전에 평가했다.
|
||||
- 공유 task group: `agent-task/m-lemonade-provider-serving-validation`.
|
||||
- `01_config_contract`: config/proto contract.
|
||||
- `02+01_openai_compatible_adapter`: Node adapter. 현재 active/archive `complete.log` 없음.
|
||||
- `03+02_field_smoke`: 이 plan. predecessor `02`는 아직 만족되지 않았다. 구현 시작 전 같은 task group의 `02_*` 또는 `02+*` active/archive `complete.log`가 있어야 한다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Lemonade 서버 설치, 모델 다운로드, 운영 제품화는 제외한다.
|
||||
- `make test-e2e` 기본 흐름에는 real Lemonade dependency를 추가하지 않는다. 새 Makefile target은 명시 실행용으로 둔다.
|
||||
- secret/header 값은 tracked script, config, docs, roadmap에 쓰지 않는다.
|
||||
- Smoke 중 발견된 큰 코드 이슈는 이 plan에서 임의 확장하지 않는다. review stub에 증거를 남기고 code-review가 후속 plan을 만들게 한다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `cloud-G07`: shell smoke orchestration, process control, real external endpoint evidence와 PASS gate가 핵심이며 terminal workflow 실패 분석이 필요할 수 있다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] 구현 시작 전 `02+01_openai_compatible_adapter`의 `complete.log`를 확인하고, 선행 adapter type/config 이름을 smoke config에 맞춘다.
|
||||
- [ ] `scripts/e2e-openai-lemonade.sh`를 추가해 fake mode와 real mode에서 Edge/Node/OpenAI-compatible route를 검증한다.
|
||||
- [ ] `Makefile`에 명시 실행 target `test-openai-lemonade`를 추가하되 `test-e2e` 기본 dependency에는 넣지 않는다.
|
||||
- [ ] fake mode smoke, real mode smoke, 관련 Go package tests를 실행한다. real mode 환경값이 없으면 review stub의 `사용자 리뷰 요청`에 기록하고 PASS를 주장하지 않는다.
|
||||
- [ ] smoke 결과 큰 코드 이슈가 발견되면 이 plan 범위를 넘겨 수정하지 말고 증거를 남겨 후속 plan으로 넘긴다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- 디렉터리명 `03+02_field_smoke`의 predecessor는 `02`뿐이다.
|
||||
- 구현 시작 전 `agent-task/m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter/complete.log` 또는 archive의 matching `02_*`/`02+*` `complete.log`를 확인한다.
|
||||
- 이 plan 본문은 추가 runtime dependency를 만들지 않는다.
|
||||
|
||||
## TEST-1 Lemonade smoke script
|
||||
|
||||
### 문제
|
||||
|
||||
`scripts/e2e-openai-ollama.sh:31-64`는 fake Ollama model/server만 만들고, Edge config도 `adapter: "ollama"`에 고정되어 있다. Lemonade/OpenAI-compatible provider를 선택해 `/v1/models`와 `/v1/chat/completions`를 검증하는 script가 없다.
|
||||
|
||||
```bash
|
||||
31 MODEL="fake-ollama-model"
|
||||
32
|
||||
33 FAKE_OLLAMA="$TMP_DIR/fake_ollama.go"
|
||||
34 cat > "$FAKE_OLLAMA" <<'EOF'
|
||||
...
|
||||
60 mux.HandleFunc("/api/tags", func(w http.ResponseWriter, _ *http.Request) {
|
||||
61 w.Header().Set("Content-Type", "application/json")
|
||||
62 _, _ = w.Write([]byte(`{"models":[{"name":"fake-ollama-model"}]}`))
|
||||
63 })
|
||||
64 mux.HandleFunc("/api/chat", func(w http.ResponseWriter, r *http.Request) {
|
||||
```
|
||||
|
||||
```bash
|
||||
123 openai:
|
||||
124 enabled: true
|
||||
125 listen: "127.0.0.1:$OPENAI_PORT"
|
||||
126 adapter: "ollama"
|
||||
127 session_id: "cline"
|
||||
128 timeout_sec: 30
|
||||
129 strict_output: false
|
||||
130 model_routes:
|
||||
131 - model: "client-request-model"
|
||||
132 adapter: "ollama"
|
||||
133 target: "$MODEL"
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- 새 `scripts/e2e-openai-lemonade.sh`를 추가한다.
|
||||
- 기본 `IOP_LEMONADE_MODE`는 `fake`다. fake mode는 temp Go server가 `/v1/models`와 `/v1/chat/completions` SSE를 제공한다.
|
||||
- real mode는 `IOP_LEMONADE_MODE=real`일 때 외부 Lemonade server를 사용한다. required env: `IOP_LEMONADE_ENDPOINT`, `IOP_LEMONADE_MODEL`. endpoint는 root 또는 trailing `/v1` 둘 다 허용하되 Node adapter config에는 root 형태로 넣는다.
|
||||
- optional auth: `IOP_LEMONADE_AUTH_HEADER_NAME` + `IOP_LEMONADE_AUTH_HEADER_VALUE`가 모두 있을 때 generated temp YAML `headers`에만 쓴다. script stdout/stderr에는 header value를 출력하지 않는다.
|
||||
- generated Edge config는 `openai.model_routes`에 client-facing model `"client-request-model"`을 두고 `adapter: "openai_compat"`, `target: "$MODEL"`, `node: "test-node"`로 보낸다.
|
||||
- generated node adapter는 선행 contract의 `openai_compat_instances` name `"lemonade-local"`, provider `"lemonade"`, endpoint, capacity/queue timeout을 사용한다.
|
||||
- 검증 단계:
|
||||
- Edge health `/healthz`.
|
||||
- Edge `/v1/models`에 client-facing model 노출.
|
||||
- Edge `/v1/chat/completions` non-streaming 응답에 sentinel 포함.
|
||||
- Edge `/v1/chat/completions` streaming 응답에 SSE content chunk와 `data: [DONE]`.
|
||||
- `go run ./apps/edge/cmd/edge smoke openai --model client-request-model ...`로 `/v1/responses` 경로도 확인.
|
||||
- fake mode sentinel은 `IOP_OPENAI_LEMONADE_OK`로 고정한다. real mode는 empty response가 아니고 model route가 성공하면 통과하되, streaming `[DONE]`은 반드시 확인한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `scripts/e2e-openai-lemonade.sh`: 새 smoke script 추가.
|
||||
- [ ] script 내부 temp config에는 secret value를 출력하지 않는다.
|
||||
- [ ] script cleanup은 Edge/Node/fake server process와 temp dir를 정리한다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- shell script 자체는 Go unit test 대신 `bash -n`과 fake mode 실행으로 검증한다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
bash -n scripts/e2e-openai-lemonade.sh
|
||||
./scripts/e2e-openai-lemonade.sh
|
||||
```
|
||||
|
||||
기대 결과: syntax check PASS, fake mode smoke PASS.
|
||||
|
||||
## TEST-2 Makefile target and command contract
|
||||
|
||||
### 문제
|
||||
|
||||
`Makefile:79-90`은 OpenAI-compatible e2e target으로 Ollama만 제공한다. Lemonade smoke는 명시적으로 실행할 target이 없다.
|
||||
|
||||
```makefile
|
||||
79 test-e2e:
|
||||
80 @echo "NOTE: test-e2e runs auxiliary smoke (Edge-Node + OpenAI) plus Control Plane-Edge wire smoke; completion still requires user-flow verification when changing runtime paths."
|
||||
81 ./scripts/e2e-smoke.sh
|
||||
82 ./scripts/e2e-openai-ollama.sh
|
||||
83 ./scripts/e2e-control-plane-edge-wire.sh
|
||||
...
|
||||
88 test-openai-ollama:
|
||||
89 ./scripts/e2e-openai-ollama.sh
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `.PHONY`에 `test-openai-lemonade`를 추가한다.
|
||||
- `test-openai-lemonade` target은 `./scripts/e2e-openai-lemonade.sh`를 실행한다.
|
||||
- `test-e2e`에는 추가하지 않는다. fake mode는 local check로 충분하지만 real Lemonade dependency를 기본 e2e에 숨기면 field smoke PASS 기준이 흐려진다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `Makefile`: `.PHONY`와 `test-openai-lemonade` target 추가.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- Makefile target은 script fake mode 실행으로 검증한다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
make test-openai-lemonade
|
||||
```
|
||||
|
||||
기대 결과: fake mode smoke PASS.
|
||||
|
||||
## TEST-3 Real Lemonade field gate and follow-up scope
|
||||
|
||||
### 문제
|
||||
|
||||
`apps/edge/internal/edgecmd/smoke_openai.go:118-124`는 generic OpenAI smoke 목적을 설명하지만 chat completions streaming을 직접 확인하지 않는다. 마일스톤은 실제 Lemonade target/model을 선택한 split-host field smoke와 streaming 안정성 확인을 요구한다.
|
||||
|
||||
```go
|
||||
118 Long: `Validate the IOP Edge OpenAI-compatible HTTP API surface.
|
||||
119 It runs a three-step diagnostics check:
|
||||
120 1. Checks GET /healthz (server status).
|
||||
121 2. Checks GET /v1/models (advertised model compatibility).
|
||||
122 3. Checks POST /v1/responses (non-streaming Responses API request processing).
|
||||
123
|
||||
124 If --base-url is not provided, it auto-discovers the OpenAI URL from the effective config's listen address.`,
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
- `scripts/e2e-openai-lemonade.sh` real mode를 최종 PASS gate로 둔다.
|
||||
- 구현 에이전트는 real mode를 아래 명령으로 실행한다. 환경값이 준비되어 있지 않으면 실행한 명령과 실패 출력을 `사용자 리뷰 요청`에 남기고 멈춘다.
|
||||
- real mode가 provider-specific code issue를 찾았고 이 plan의 script/config 소규모 수정으로 해결할 수 없으면 PASS를 주장하지 않는다. review stub의 `계획 대비 변경 사항`과 `검증 결과`에 증거를 남겨 code-review가 후속 plan을 만들게 한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `scripts/e2e-openai-lemonade.sh`: real mode validation branch 추가.
|
||||
- [ ] `CODE_REVIEW-cloud-G07.md`: real mode 실행 결과 또는 사용자 리뷰 요청 근거 기록.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- real Lemonade 환경은 unit test로 대체하지 않는다. Roadmap `field-smoke` PASS에는 real mode command output이 필요하다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
IOP_LEMONADE_MODE=real ./scripts/e2e-openai-lemonade.sh
|
||||
```
|
||||
|
||||
기대 결과: env가 준비된 경우 real Lemonade smoke PASS. env가 없으면 사용자 리뷰 요청에 blocker evidence를 남기고 PASS 금지.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `scripts/e2e-openai-lemonade.sh` | TEST-1, TEST-3 |
|
||||
| `Makefile` | TEST-2 |
|
||||
| `CODE_REVIEW-cloud-G07.md` | TEST-3 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
bash -n scripts/e2e-openai-lemonade.sh
|
||||
./scripts/e2e-openai-lemonade.sh
|
||||
make test-openai-lemonade
|
||||
IOP_LEMONADE_MODE=real ./scripts/e2e-openai-lemonade.sh
|
||||
go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/edgecmd ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters
|
||||
git diff --check
|
||||
```
|
||||
|
||||
기대 결과: 모든 명령이 exit 0. real mode 환경값이 없거나 endpoint/model/auth 준비가 안 되어 있으면 PASS 금지, `사용자 리뷰 요청` 작성.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
Loading…
Reference in a new issue