<!-- task=m-node-multi-target-serving-foundation/04+01,02_engine_profiles plan=0 tag=API -->

# Code Review Reference - API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> Fill implementation-owned sections and verification output, then stop with active files in place.
> Do not ask the user directly; record user-only blockers in `사용자 리뷰 요청`.
> Finalization is review-agent-only.

## 개요

date=2026-06-11
task=m-node-multi-target-serving-foundation/04+01,02_engine_profiles, plan=0, tag=API

## Roadmap Targets

- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/node-multi-target-serving-foundation.md`
- Task ids:
  - `engine-profiles`: Ollama는 여러 `base_url`/model target과 target별 `context_size`를 지원하고, vLLM/SGLang 같은 OpenAI-compatible inference engine 연결을 같은 Node 통로에서 확장할 수 있는 provider/profile 기준이 정리되어 있다.
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.

provider profile 선택이 target별 endpoint/context를 정확히 고정하고, vLLM/SGLang 확장 기준이 과도하게 넓어지지 않았는지 확인한다.

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [API-1] Provider Profile Execution | [x] |

## 구현 체크리스트

- [x] Ollama multi profile이 endpoint/model/context를 target별로 선택한다.
- [x] vLLM 또는 공통 OpenAI-compatible provider/profile baseline이 registry namespace 위에서 확장 가능하게 정리된다.
- [x] provider capabilities가 profile/target 식별자를 모호하지 않게 반환한다.
- [x] Ollama/vLLM/factory 테스트가 multi endpoint/profile 선택을 검증한다.
- [x] 최종 검증 명령을 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## 코드리뷰 전용 체크리스트

- [x] `코드리뷰 결과`에 판정을 append한다.
- [x] active `CODE_REVIEW-cloud-G08.md`를 `code_review_cloud_G08_N.log`로 아카이브한다.
- [x] active `PLAN-cloud-G07.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
- [ ] PASS이면 `complete.log` 작성 후 archive 이동한다.
- [x] WARN/FAIL이면 후속 상태를 작성한다.

## 계획 대비 변경 사항

- `runtime/types.go`에 `InstanceKey string` 필드를 `Capabilities`에 추가했다. PLAN에는 "필요한 profile route metadata 최소 추가"로 명시되어 있었으며, 이 필드가 다중 인스턴스 환경에서 capabilities 식별을 명확하게 한다.
- `ollamaOptionsFromInput`에서 `num_ctx` 주입 동작을 변경했다. 기존 코드는 config의 `contextSize`로 항상 덮어썼지만, domain rule(node/rules.md)에 따라 요청 input에 `options.num_ctx`가 있으면 덮어쓰지 않도록 수정했다. 관련 테스트(`TestOllamaExecutePassesOptionsAndTopLevelFields`)도 domain rule에 맞게 수정했다.
- vLLM을 "disabled error"에서 완전히 활성화된 adapter로 전환했다. factory/blackbox/internal 테스트 3개를 "disabled → enabled" 검증으로 교체했다.

## 주요 설계 결정

### Ollama 멀티 인스턴스

각 `OllamaInstanceConf`는 Edge의 `BuildConfigPayload`에서 별도의 `AdapterConfig`(name 포함)로 직렬화되고, Node의 `BuildFromPayload`에서 별도의 `Ollama` struct로 생성되어 registry에 instance key로 등록된다. `New(cfg, logger, instanceKey...)` 시그니처로 instance name을 주입해 `Capabilities.InstanceKey`에 반영했다.

### vLLM SSE 스트리밍

`/v1/chat/completions`에 `stream: true`로 POST하고 SSE 스트림을 `bufio.Scanner`로 줄 단위 파싱한다. `data: [DONE]`을 complete 이벤트 트리거로 사용하는 OpenAI 표준 방식을 따른다. SGLang도 동일한 엔드포인트를 노출하므로 vllm adapter를 그대로 재사용할 수 있다.

### num_ctx 우선순위

domain rule: "context_size는 options.num_ctx default로만 주입한다. 요청 input에 명시된 options.num_ctx가 있으면 덮어쓰지 않는다." 에 맞게 `ollamaOptionsFromInput`을 수정했다. input의 `options.num_ctx`가 없을 때만 adapter의 `contextSize`를 기본값으로 주입한다.

## 사용자 리뷰 요청

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- Ollama request가 target별 fake server로 정확히 나뉘는지 확인한다.
- target별 `context_size`가 request option으로 반영되는지 확인한다.
- vLLM/SGLang 기준선이 disabled dead-end가 아니라 후속 provider 추가 경로를 만든다.

## 검증 결과

### API-1 중간 검증

```
$ go test -count=1 ./apps/node/internal/adapters/ollama/... ./apps/node/internal/adapters/vllm/... ./apps/node/internal/adapters/...
ok  	iop/apps/node/internal/adapters/ollama	0.017s
ok  	iop/apps/node/internal/adapters/vllm	0.010s
ok  	iop/apps/node/internal/adapters	0.005s
ok  	iop/apps/node/internal/adapters/cli	42.219s
ok  	iop/apps/node/internal/adapters/cli/status	39.840s
```

### 최종 검증

```
$ go test -count=1 ./apps/node/...
ok  	iop/apps/node/cmd/node	0.010s
ok  	iop/apps/node/internal/adapters	0.005s
ok  	iop/apps/node/internal/adapters/cli	42.364s
ok  	iop/apps/node/internal/adapters/cli/status	39.859s
ok  	iop/apps/node/internal/adapters/ollama	0.008s
ok  	iop/apps/node/internal/adapters/vllm	0.009s
ok  	iop/apps/node/internal/bootstrap	0.263s
ok  	iop/apps/node/internal/node	0.012s
ok  	iop/apps/node/internal/router	0.005s
ok  	iop/apps/node/internal/store	0.035s
ok  	iop/apps/node/internal/terminal	0.429s
ok  	iop/apps/node/internal/transport	5.139s
```

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Pass
  - completeness: Fail
  - test coverage: Fail
  - API contract: Warn
  - code quality: Pass
  - plan deviation: Pass
  - verification trust: Pass
- 발견된 문제:
  - Required: `apps/node/internal/adapters/adapters_blackbox_test.go:84`의 vLLM factory 테스트와 `apps/node/internal/adapters/factory_internal_test.go:327`의 legacy fallback 테스트가 `BuildFromPayload` 결과의 등록 여부만 확인한다. 계획/리뷰 체크리스트는 "Ollama/vLLM/factory 테스트가 multi endpoint/profile 선택을 검증"해야 한다고 되어 있지만, factory가 만든 adapter가 실제 endpoint/model/context를 보존해 실행하는지는 검증하지 않는다. `BuildFromPayload`로 두 Ollama 인스턴스와 vLLM 인스턴스를 만들고 `Execute`까지 호출해 각 fake server가 서로 다른 endpoint/model/context를 받는지 확인하는 테스트를 추가한다.
  - Suggested: `apps/node/internal/node/node.go:219`의 capabilities command 응답은 `caps.InstanceKey`를 버리고 `adapter`, `targets`, `max_concurrency`만 반환한다. adapter 구현은 instance key를 채우지만 운영 표면의 `/capabilities` 결과에서는 profile/instance 식별자가 보이지 않는다. `instance_key`를 result map에 포함하고 exact instance key 요청 테스트에서 이를 검증하면 profile 식별자 계약이 더 명확해진다.
- 다음 단계: FAIL이므로 active plan/review를 로그로 아카이브하고, 위 Required/Suggested 항목을 좁게 처리하는 후속 `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G08.md`를 작성한다.
