<!-- task=m-openai-compatible-think-control/02+01_node_provider_mapping 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.
> Start only after predecessor `01_edge_request_contract` has `complete.log`.
> Fill implementation-owned sections, then stop with active files in place and report ready for review. Finalization is review-agent-only.

## 개요

date=2026-07-03
task=m-openai-compatible-think-control/02+01_node_provider_mapping, plan=0, tag=API

## Roadmap Targets

- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
- Task ids:
  - `provider-mapping`: Edge run input과 Node `openai_compat` adapter가 요청별 think 설정을 provider별 top-level field 또는 내부 `chat_template_kwargs.enable_thinking`으로 매핑한다.
  - `default-compat`: think 관련 필드가 생략된 기존 요청은 현재 provider 기본 thinking 동작을 유지한다.
- Completion mode: check-on-pass

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

> **[REVIEW AGENT ONLY]** 구현 에이전트는 종결 절차를 실행하지 않는다.

실제 provider body mapping, omitted-field absence, predecessor dependency 준수, 검증 출력 일치를 확인한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [API-1] Provider-Specific Think Body Mapping | [x] |
| [API-2] Default Compatibility Regression | [x] |

## 구현 체크리스트

- [x] `01_edge_request_contract` predecessor `complete.log`를 확인한 뒤 시작한다.
- [x] `openai_compat` adapter request body builder가 provider label을 받아 think-control input을 provider별 body로 매핑하도록 바꾼다. 검증: vLLM Qwen3, vLLM-MLX, Lemonade mock이 같은 요청 플래그에 대해 기대 request body를 받는다.
- [x] omitted think-control field가 `think`, `reasoning_effort`, `thinking_token_budget`, `chat_template_kwargs`를 만들지 않는 회귀 테스트를 추가한다. 검증: 기존 chat/tool/provider pool targeted tests가 통과한다.
- [x] `go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

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

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] active plan/review 파일을 `.log`로 아카이브한다.
- [ ] PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
- [x] WARN/FAIL이면 다음 active plan/review 파일 또는 정당한 `USER_REVIEW.md`를 작성한다.

## 계획 대비 변경 사항

계획 대비 변경 사항이 없습니다.

## 주요 설계 결정

- `buildRequestBody` 함수를 `Adapter`의 메서드 `(a *Adapter) buildRequestBody(...)`로 승격하여 `a.provider`에 따른 매핑 분기를 안전하게 처리하였습니다.
- vLLM 및 vLLM-MLX 프로바이더의 경우, `chat_template_kwargs` 내부에 `enable_thinking`과 `thinking_token_budget`을 맵으로 변환 및 삽입하고, `"none"` 이외의 `reasoning_effort`가 들어오면 `unsupported think control` 에러를 반환하게 설계하였습니다.
- Lemonade 프로바이더는 `think`와 `reasoning_effort`, `thinking_token_budget` 필드를 그대로 최상위(top-level) 필드로 전달하도록 매핑하였습니다.
- `reasoning_effort=none` 값은 `think=false` 정책과 동일하게 취급하여 매핑 후, 기존 `reasoning_effort` 필드는 프로바이더 body에서 생략하였습니다.
- think-control 필드들이 생략(omitted)된 요청의 경우 `chat_template_kwargs`를 오염시키지 않도록 예외 처리하여, 기존 기본 추론 및 툴 호출 흐름에 영향이 없도록 설계하였습니다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._

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

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

- vLLM/vLLM-MLX body가 raw Edge wrapper 없이 adapter 내부 `chat_template_kwargs`를 생성하는지 확인한다.
- Lemonade mapping과 omitted default absence가 별도 테스트되는지 확인한다.
- `tools`/`tool_choice` fallback behavior가 깨지지 않았는지 확인한다.

## 검증 결과

각 검증 단계를 로컬 워크스페이스 환경에서 성공적으로 수행하였습니다.

### API-1 중간 검증
```bash
$ go test -count=1 ./apps/node/internal/adapters/openai_compat
ok  	iop/apps/node/internal/adapters/openai_compat	0.010s
```

### API-2 중간 검증
```bash
$ go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai
ok  	iop/apps/node/internal/adapters/openai_compat	0.010s
ok  	iop/apps/edge/internal/openai	1.567s
```

### 최종 검증
```bash
$ go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai
ok  	iop/apps/node/internal/adapters/openai_compat	0.010s
ok  	iop/apps/edge/internal/openai	1.567s
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - implementation deviation: Fail
  - verification trust: Pass
  - spec conformance: Fail
- 발견된 문제:
  - Required: `apps/node/internal/adapters/openai_compat/openai_compat.go:514`에서 vLLM/vLLM-MLX 요청에 `thinking_token_budget`만 있는 경우 `chat_template_kwargs`는 생성하지만 `enable_thinking=true`를 같이 넣지 않습니다. SDD state machine은 유효한 `thinking_token_budget`을 `think-enabled` 진입 조건으로 정의하고, dev provider는 기본 `enable_thinking=false`로 운영될 수 있어 budget-only 요청이 실제 thinking 활성화 없이 provider default에 맡겨질 수 있습니다. 수정: budget을 지원할 정책이면 명시 disable이 아닌 budget/positive enable 요청에서 `enable_thinking=true`를 설정하고, 지원하지 않을 정책이면 `unsupported think control` 오류로 일관되게 거부하세요.
  - Required: `apps/node/internal/adapters/openai_compat/openai_compat_test.go:620` 이후 추가된 provider body 테스트가 `think=false`와 `reasoning_effort=none`은 확인하지만 plan이 요구한 `think=true` 및 budget/unsupported budget 매핑 테스트를 포함하지 않습니다. 위 budget-only 회귀를 잡는 단위 테스트가 없으므로 SDD S02/S04 evidence가 불충분합니다. 수정: vLLM, vLLM-MLX, Lemonade의 `think=true` body와 budget 정책을 provider mock으로 고정하고, unsupported budget 정책을 선택한 경우 error path를 함께 검증하세요.
- 다음 단계: FAIL 후속 `PLAN-local-G04.md`와 `CODE_REVIEW-local-G04.md`를 작성한다.
