chore: update knowledge-tool-optimization-extension phase, SDD, and openai changes
This commit is contained in:
parent
8838e0aced
commit
3e1248d584
17 changed files with 2066 additions and 3 deletions
|
|
@ -19,6 +19,10 @@ Ollama serving 경로와 운영 기반이 안정화된 뒤, 단계 호출, tool/
|
|||
- 경로: `agent-roadmap/archive/phase/knowledge-tool-optimization-extension/milestones/tool-call-runtime-validation-retry.md`
|
||||
- 요약: 명시적 `tools[]`/`tool_choice` 계약이 있는 OpenAI-compatible 요청에서 IOP 내부 provider 응답의 마지막 tool call을 검증하고, schema 불일치 시 같은 요청을 bounded exact replay로 재시도하는 runtime-only MVP를 구현한다.
|
||||
|
||||
- [계획] OpenAI-compatible Think 제어 MVP
|
||||
- 경로: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- 요약: OpenAI-compatible Chat Completions 요청에서 thinking/reasoning 생성과 응답 노출을 요청별로 제어하고 provider별 option 매핑과 unsupported 정책을 구현한다.
|
||||
|
||||
- [스케치] 단계 호출과 검증 최적화 MVP
|
||||
- 경로: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/knowledge-tool-validation-optimization.md`
|
||||
- 요약: 요청 의도 분석, 실제 작업, 검증/schema 강제, 오류 시 회귀를 단계 호출 실행 모드의 MVP 후보로 스케치한다.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
# Milestone: OpenAI-compatible Think 제어 MVP
|
||||
|
||||
## 위치
|
||||
|
||||
- Roadmap: `agent-roadmap/ROADMAP.md`
|
||||
- Phase: `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`
|
||||
|
||||
## 목표
|
||||
|
||||
OpenAI-compatible Chat Completions 요청에서 thinking/reasoning 생성을 요청별로 켜고 끌 수 있게 한다.
|
||||
provider runtime은 reasoning-capable 상태로 유지하되, Edge가 공개 요청 필드를 검증하고 Node provider adapter가 vLLM, vLLM-MLX, Lemonade의 지원 필드로 매핑한다.
|
||||
호출자가 reasoning 생성을 끄거나 reasoning 응답 노출만 숨길 수 있어야 하며, 지원하지 않는 조합은 조용히 무시하지 않고 명확한 compatibility error 또는 정의된 fallback으로 처리한다.
|
||||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
|
||||
## 승격 조건
|
||||
|
||||
- 없음
|
||||
|
||||
## 구현 잠금
|
||||
|
||||
- 상태: 해제
|
||||
- SDD: 필요
|
||||
- SDD 문서: `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- SDD 사유: OpenAI-compatible request schema, provider option 매핑, streaming response 노출 정책이 바뀌는 API 계약 Milestone이다.
|
||||
- 잠금 해제 조건:
|
||||
- [x] SDD 잠금이 해제되어 있다.
|
||||
- [x] SDD 사용자 리뷰가 없거나 승인/해결되었다.
|
||||
- [x] Acceptance Scenario가 Milestone 기능 Task와 연결되어 있다.
|
||||
- [x] Evidence Map이 완료 시 `Roadmap Completion`과 최종 검증 evidence로 검증 가능하게 연결되어 있다.
|
||||
- 결정 필요: 없음
|
||||
|
||||
## 범위
|
||||
|
||||
- `/v1/chat/completions` 요청의 thinking/reasoning 제어 field 수신, 검증, 내부 run input 전달
|
||||
- `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`의 의미와 충돌 정책
|
||||
- raw `chat_template_kwargs`는 공개 request field로 직접 열지 않고, Edge/Node가 provider별 내부 option으로 생성하는 매핑 정책
|
||||
- vLLM Qwen3, vLLM-MLX Qwen3, Lemonade provider의 reasoning-capable runtime에 대한 요청별 option 매핑
|
||||
- streaming/non-streaming 응답의 `reasoning_content` 노출/억제 정책
|
||||
- OpenAI-compatible 계약 문서, edge/node 단위 테스트, dev-runtime smoke
|
||||
|
||||
## 기능
|
||||
|
||||
### Epic: [think-control] Request-Level Think Control
|
||||
|
||||
OpenAI-compatible 호출자가 요청 단위로 thinking/reasoning 생성과 노출을 제어하는 capability를 묶는다.
|
||||
|
||||
- [ ] [request-contract] `/v1/chat/completions`가 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`을 허용하고 타입, 범위, 충돌 조합을 검증한다. 검증: 알 수 없는 provider 전용 wrapper는 계속 400으로 거부되고, 허용 필드는 Edge request decoder와 계약 테스트를 통과한다.
|
||||
- [ ] [provider-mapping] Edge run input과 Node `openai_compat` adapter가 요청별 think 설정을 provider별 top-level field 또는 내부 `chat_template_kwargs.enable_thinking`으로 매핑한다. 검증: vLLM Qwen3, vLLM-MLX, Lemonade mock이 같은 요청 플래그에 대해 기대 request body를 받는다.
|
||||
- [ ] [reasoning-visibility] `include_reasoning=false`가 provider reasoning 생성 여부와 별개로 OpenAI-compatible 응답의 `reasoning_content` 노출을 억제한다. 검증: stream과 non-stream 응답 모두 reasoning delta/message field가 빠지고 content/tool output은 유지된다.
|
||||
- [ ] [default-compat] think 관련 필드가 생략된 기존 요청은 현재 provider 기본 thinking 동작을 유지한다. 검증: 기존 chat completion, tool calling, provider pool 테스트가 회귀 없이 통과한다.
|
||||
- [ ] [dev-smoke] dev-runtime 세 provider(mac vLLM-MLX, GX10 vLLM, OneXPlayer Lemonade)에서 `think=false`, 기본값, `include_reasoning=false` smoke가 통과한다. 검증: provider raw trace와 Edge OpenAI-compatible SSE를 비교해 reasoning 생성/노출 상태가 기대와 일치한다.
|
||||
- [ ] [contract-docs] OpenAI-compatible 계약 문서와 dev 운영 문서가 요청별 thinking/reasoning 제어 field, provider별 unsupported 정책, 기본값을 설명한다.
|
||||
|
||||
## 완료 리뷰
|
||||
|
||||
- 상태: 없음
|
||||
- 요청일: 없음
|
||||
- 완료 근거: 계획 Milestone이며 기능 Task가 아직 충족되지 않았다.
|
||||
- 검토 항목: 모든 기능 Task의 테스트와 dev-runtime smoke evidence가 필요하다.
|
||||
- agent-ui 상태 반영: 해당 없음
|
||||
- 리뷰 코멘트: 없음
|
||||
|
||||
## 범위 제외
|
||||
|
||||
- `/v1/responses`의 streaming 지원 또는 reasoning output item 확장
|
||||
- provider runtime 기동 옵션 자동 조정과 lifecycle 관리
|
||||
- hidden reasoning token의 정확한 usage 산출
|
||||
- 단계 호출 planner/generator/verifier 모드 전반의 제품 UX
|
||||
- raw provider payload 장기 저장과 운영 ledger schema 구현
|
||||
|
||||
## 작업 컨텍스트
|
||||
|
||||
- 관련 경로: `apps/edge/internal/openai`, `apps/node/internal/adapters/openai_compat`, `agent-contract/outer/openai-compatible-api.md`, `docs/openai-compatible-api-contract.md`, `agent-test/dev`
|
||||
- 표준선(선택): provider runtime은 reasoning-capable 상태로 띄우고, 요청별 제어는 OpenAI-compatible Edge request 계약에서 검증한 뒤 provider별 지원 option으로 매핑한다.
|
||||
- 표준선(선택): 공개 request 표면은 provider raw wrapper를 그대로 열지 않고, IOP가 검증 가능한 OpenAI-compatible field와 내부 provider option 매핑으로 둔다.
|
||||
- 선행 작업: OpenAI-compatible reasoning raw trace와 dev-runtime provider reasoning 활성화
|
||||
- 후속 작업: 단계 호출과 검증 최적화 MVP, 요청 실행 로그와 Usage Ledger 기반
|
||||
- 확인 필요: 없음
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
# SDD: OpenAI-compatible Think 제어 MVP
|
||||
|
||||
## 위치
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Phase: `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`
|
||||
|
||||
## 상태
|
||||
|
||||
[승인됨]
|
||||
|
||||
## SDD 잠금
|
||||
|
||||
- 상태: 해제
|
||||
- 사용자 리뷰: 없음
|
||||
- 잠금 항목:
|
||||
- 없음
|
||||
|
||||
## 문제 / 비목표
|
||||
|
||||
- 문제: dev-runtime provider는 reasoning-capable 상태로 기동되어 있지만, 현재 OpenAI-compatible 요청자는 요청 단위로 thinking 생성을 끄거나 reasoning 응답 노출을 숨길 수 없다. 공개 API가 provider별 raw option을 그대로 열면 호환성과 검증 경계가 흐려지므로, Edge가 검증 가능한 field를 받고 Node adapter가 provider별 option으로 매핑해야 한다.
|
||||
- 비목표:
|
||||
- `/v1/responses`의 streaming 또는 reasoning output item 확장
|
||||
- provider runtime 기동/lifecycle 자동화
|
||||
- usage ledger의 hidden reasoning token 정산
|
||||
- 단계 호출 planner/generator/verifier 제품 UX 확정
|
||||
|
||||
## Source of Truth
|
||||
|
||||
| 영역 | 기준 | 메모 |
|
||||
|------|------|------|
|
||||
| Roadmap | `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md` | 기능 Task와 완료 evidence 기준 |
|
||||
| Code | `apps/edge/internal/openai`, `apps/node/internal/adapters/openai_compat` | request decode, run input, provider request body, response filtering 구현 기준 |
|
||||
| Contract | `agent-contract/outer/openai-compatible-api.md` | 공개 OpenAI-compatible request/response 계약 |
|
||||
| External Provider | vLLM Qwen3, vLLM-MLX Qwen3, Lemonade | provider별 reasoning option 지원 여부는 dev smoke와 mock 테스트로 확인 |
|
||||
| User Decision | 없음 | 공개 field와 기본값은 기존 OpenAI-compatible 원칙과 provider 동작을 기준으로 확정 가능 |
|
||||
|
||||
## State Machine
|
||||
|
||||
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|
||||
|------|-----------|-----------|------|
|
||||
| default | 요청에 think 관련 field가 없다 | provider-default | 기존 요청 호환성 유지 |
|
||||
| think-disabled | `think=false` 또는 `reasoning_effort=none`이 유효하다 | content-only | Edge request validation, provider request body |
|
||||
| think-enabled | `think=true` 또는 thinking budget/effort가 유효하다 | reasoning-capable | provider runtime capability와 request body |
|
||||
| reasoning-hidden | `include_reasoning=false`가 유효하다 | response-filtered | Edge stream/non-stream response writer |
|
||||
| unsupported | provider가 요청 조합을 지원하지 않거나 field 타입/범위가 틀리다 | error | OpenAI-compatible 400 error 또는 명시 fallback 정책 |
|
||||
|
||||
## Interface Contract
|
||||
|
||||
- 계약 원문: `agent-contract/outer/openai-compatible-api.md`
|
||||
- 입력:
|
||||
- `think`: optional boolean. `false`는 reasoning/thinking 생성을 끄도록 요청하고, `true`는 provider가 지원하면 thinking 생성을 명시 활성화한다.
|
||||
- `reasoning_effort`: optional string. `none`은 `think=false`와 같은 disable 의미로 처리하고, 그 외 값은 provider가 지원하는 경우에만 전달한다.
|
||||
- `thinking_token_budget`: optional non-negative integer. provider가 지원하면 thinking budget으로 전달하고, 지원하지 않으면 명시 error 또는 정의된 fallback을 반환한다.
|
||||
- `include_reasoning`: optional boolean. `false`이면 provider가 reasoning을 생성하더라도 OpenAI-compatible 응답의 `reasoning_content`를 노출하지 않는다.
|
||||
- 출력:
|
||||
- streaming: `include_reasoning`이 true 또는 생략이면 reasoning delta를 `delta.reasoning_content`로 반환하고, false이면 reasoning delta를 쓰지 않는다.
|
||||
- non-streaming: `include_reasoning`이 true 또는 생략이면 `message.reasoning_content`를 반환할 수 있고, false이면 반환하지 않는다.
|
||||
- content/tool output: reasoning 노출 정책과 별개로 기존 content/tool_calls 계약을 유지한다.
|
||||
- 금지:
|
||||
- 공개 request에 raw `options` wrapper 또는 provider raw `chat_template_kwargs` wrapper를 그대로 추가하지 않는다.
|
||||
- provider가 지원하지 않는 think/budget 조합을 조용히 무시하지 않는다.
|
||||
- `metadata`에 think 제어 field를 넣어 공개 API 의미를 우회하지 않는다.
|
||||
- `include_reasoning=false`를 provider raw trace나 진단 로그 비활성화로 해석하지 않는다.
|
||||
|
||||
## Acceptance Scenarios
|
||||
|
||||
| ID | Milestone Task | Given | When | Then |
|
||||
|----|----------------|-------|------|------|
|
||||
| S01 | `request-contract` | Chat Completions 요청에 `think=false`가 있다 | Edge가 요청을 decode한다 | 요청은 400으로 거부되지 않고 run input에 think disable 의미가 들어간다 |
|
||||
| S02 | `provider-mapping` | provider pool 후보가 vLLM Qwen3, vLLM-MLX, Lemonade다 | `think=false` 요청을 dispatch한다 | 각 provider request body가 지원 field 또는 내부 `chat_template_kwargs.enable_thinking=false`로 매핑된다 |
|
||||
| S03 | `reasoning-visibility` | provider가 reasoning delta를 반환한다 | 요청에 `include_reasoning=false`가 있다 | Edge OpenAI-compatible stream/non-stream 응답에는 `reasoning_content`가 없다 |
|
||||
| S04 | `default-compat` | 기존 caller가 think field를 생략한다 | Chat Completions를 호출한다 | 현재 provider 기본 thinking 동작과 기존 content/tool behavior가 유지된다 |
|
||||
| S05 | `dev-smoke` | dev-runtime 세 provider가 connected/healthy다 | 기본값, `think=false`, `include_reasoning=false` smoke를 실행한다 | provider raw trace와 Edge SSE의 reasoning 생성/노출 상태가 기대와 일치한다 |
|
||||
| S06 | `contract-docs` | 구현이 완료되었다 | 계약 문서와 dev 문서를 확인한다 | 공개 field, 기본값, provider unsupported 정책이 문서화되어 있다 |
|
||||
|
||||
## Evidence Map
|
||||
|
||||
| Scenario | Required Evidence | `agent-task` 연결 | 완료 Evidence 기대 |
|
||||
|----------|-------------------|------------------|---------------------------|
|
||||
| S01 | Edge OpenAI request decoder/unit test | `agent-task/m-openai-compatible-think-control/...` | `Roadmap Completion`에 `request-contract`와 테스트 명령 기록 |
|
||||
| S02 | Node `openai_compat` request body unit test와 provider별 mock | `agent-task/m-openai-compatible-think-control/...` | `Roadmap Completion`에 `provider-mapping`과 mock 검증 기록 |
|
||||
| S03 | stream/non-stream response filtering test | `agent-task/m-openai-compatible-think-control/...` | `Roadmap Completion`에 `reasoning-visibility`와 테스트 명령 기록 |
|
||||
| S04 | 기존 chat/tool/provider pool regression test | `agent-task/m-openai-compatible-think-control/...` | `Roadmap Completion`에 `default-compat`와 회귀 테스트 기록 |
|
||||
| S05 | dev-runtime smoke 결과와 raw trace/Edge SSE 비교 요약 | `agent-task/m-openai-compatible-think-control/...` | `Roadmap Completion`에 `dev-smoke`와 provider별 관측 기록 |
|
||||
| S06 | 계약 문서 diff와 문서 검증 | `agent-task/m-openai-compatible-think-control/...` | `Roadmap Completion`에 `contract-docs`와 수정 문서 경로 기록 |
|
||||
|
||||
## Cross-repo Dependencies
|
||||
|
||||
- 없음
|
||||
|
||||
## Drift Check
|
||||
|
||||
- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
|
||||
- [x] Evidence Map이 code-review/complete.log에서 검증 가능하다.
|
||||
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
|
||||
- [x] 사용자 리뷰가 필요한 항목은 `USER_REVIEW.md`에만 남겼다.
|
||||
|
||||
## 사용자 리뷰 이력
|
||||
|
||||
- 없음
|
||||
|
||||
## 작업 컨텍스트
|
||||
|
||||
- 표준선: OpenAI-compatible 공개 표면은 provider raw wrapper가 아니라 검증 가능한 top-level field를 받으며, provider별 세부 option은 Edge/Node adapter 내부 매핑으로 처리한다.
|
||||
- 후속 SDD: 없음
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<!-- task=m-openai-compatible-think-control/01_edge_request_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 selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-03
|
||||
task=m-openai-compatible-think-control/01_edge_request_contract, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `request-contract`: `/v1/chat/completions`가 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`을 허용하고 타입, 범위, 충돌 조합을 검증한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. active `CODE_REVIEW-local-G04.md`와 `PLAN-local-G04.md`를 `.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
4. PASS이고 task group이 `m-openai-compatible-think-control`이면 완료 이벤트 메타데이터를 보고한다. roadmap 수정은 런타임 책임이다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Edge Request Decode And Validation | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] `chatCompletionRequest`에 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning` 필드를 추가하고 `/v1/chat/completions` decoder allowlist와 validation을 갱신한다. 검증: unknown provider wrapper는 계속 400, 허용 필드는 Edge request decoder/unit test 통과.
|
||||
- [x] `runInput`이 validated think-control 값을 내부 input map에 보존하되, omitted field는 기존 provider default 동작을 유지하도록 어떤 key도 추가하지 않는다.
|
||||
- [x] `apps/edge/internal/openai/server_test.go`에 허용 필드, 범위/충돌 실패, unknown wrapper 회귀 테스트를 추가/갱신한다.
|
||||
- [x] `go test -count=1 ./apps/edge/internal/openai`를 실행한다.
|
||||
- [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_{review_lane}_GNN_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
|
||||
- [x] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [x] PASS이면 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다.
|
||||
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고하고 roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 파일 또는 정당한 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
없음. 계획서에 기술된 모든 사항을 충족하여 정상 구현하였습니다.
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
`chatCompletionRequest` 에 JSON 매핑과 decoding 시 raw 필드 validation(`validateThinkControl`)을 두어 strict-output 상황과 dynamic field validation을 결합시켰습니다. 생략된 필드가 internal map에 누출되지 않도록 구조체 포인터 멤버 변수로 널 체크를 적용하여 runInput 맵에 보존하도록 구현했습니다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `think`는 더 이상 unsupported가 아니지만 `options`, `format`, `keep_alive`는 계속 400인지 확인한다.
|
||||
- omitted think-control field가 run input에 새 key를 만들지 않는지 확인한다.
|
||||
- invalid effort/budget conflict가 400으로 테스트되는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/openai
|
||||
ok iop/apps/edge/internal/openai 1.568s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/openai
|
||||
ok iop/apps/edge/internal/openai 1.568s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: PASS
|
||||
- 차원별 평가:
|
||||
- correctness: Pass
|
||||
- completeness: Pass
|
||||
- test coverage: Pass
|
||||
- API contract: Pass
|
||||
- code quality: Pass
|
||||
- implementation deviation: Pass
|
||||
- verification trust: Pass
|
||||
- spec conformance: Pass
|
||||
- 발견된 문제: 없음
|
||||
- 다음 단계: PASS 종결 절차를 진행한다. `complete.log` 작성 후 task directory를 archive로 이동하고, `m-openai-compatible-think-control` 완료 이벤트 메타데이터를 보고한다.
|
||||
|
||||
### 리뷰 메모
|
||||
|
||||
- SDD S01(`request-contract`) 기준으로 `/v1/chat/completions` decoder allowlist, think-control validation, run input key 보존, omitted field 미삽입 테스트를 확인했다.
|
||||
- 리뷰 중 `gofmt`로 테스트 파일의 공백 포맷만 정리했다.
|
||||
- 최종 재검증: `go test -count=1 ./apps/edge/internal/openai` PASS (`ok iop/apps/edge/internal/openai 1.564s`), `git diff --check -- apps/edge/internal/openai/types.go apps/edge/internal/openai/chat_handler.go apps/edge/internal/openai/server_test.go` PASS.
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# Complete - m-openai-compatible-think-control/01_edge_request_contract
|
||||
|
||||
## 완료 일시
|
||||
|
||||
2026-07-03T05:27:49Z
|
||||
|
||||
## 요약
|
||||
|
||||
Edge `/v1/chat/completions` think-control request contract 구현을 1회 리뷰 루프로 PASS 완료했다.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_local_G04_0.log` | `code_review_local_G04_0.log` | PASS | SDD S01 `request-contract` 기준 decoder, validation, run input 보존, 단위 테스트 evidence 충족 |
|
||||
|
||||
## 구현/정리 내용
|
||||
|
||||
- `chatCompletionRequest`와 decoder allowlist에 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`을 추가하고 범위/충돌 검증을 연결했다.
|
||||
- validated think-control 값만 `SubmitRunRequest.Input`에 보존하고, omitted field는 내부 input key를 만들지 않도록 했다.
|
||||
- 허용 필드, invalid 조합, 기존 unsupported wrapper 회귀 테스트를 추가/갱신했다.
|
||||
- 리뷰 중 `gofmt`로 테스트 파일 공백 포맷만 정리했다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
- `go test -count=1 ./apps/edge/internal/openai` - PASS; `ok iop/apps/edge/internal/openai 1.564s`
|
||||
- `git diff --check -- apps/edge/internal/openai/types.go apps/edge/internal/openai/chat_handler.go apps/edge/internal/openai/server_test.go` - PASS; 출력 없음
|
||||
|
||||
## Roadmap Completion
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Completed task ids:
|
||||
- `request-contract`: PASS; evidence=`agent-task/archive/2026/07/m-openai-compatible-think-control/01_edge_request_contract/plan_local_G04_0.log`, `agent-task/archive/2026/07/m-openai-compatible-think-control/01_edge_request_contract/code_review_local_G04_0.log`; verification=`go test -count=1 ./apps/edge/internal/openai`
|
||||
- Not completed task ids: 없음
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
||||
## 후속 작업
|
||||
|
||||
- 없음
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
<!-- task=m-openai-compatible-think-control/01_edge_request_contract plan=0 tag=API -->
|
||||
|
||||
# Plan - API Edge Request Contract
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지가 구현 완료다. 검증을 실행하고 실제 출력/변경 내용을 채운 뒤 active 파일을 그대로 두고 review ready로 보고한다. 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 막으면 review stub의 `사용자 리뷰 요청`만 채우고 멈춘다. 직접 사용자에게 묻거나 `USER_REVIEW.md`, `complete.log`, archive 파일을 만들지 않는다.
|
||||
|
||||
## 배경
|
||||
|
||||
현재 Milestone은 승인된 SDD를 통해 Chat Completions의 요청별 thinking 제어 필드를 공개 API로 열기로 했다. 현재 Edge decoder는 `think`를 unsupported field로 거부하고, strict-output 내부 경로만 `input["think"]=false`를 만든다. 먼저 공개 request contract와 내부 run input key를 고정해야 Node provider mapping과 response filtering split이 같은 기준을 쓸 수 있다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 선택된 Milestone lock decision이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 중 직접 사용자 질문, 선택지 제시, `request_user_input` 호출은 금지이며, code-review가 검증 후 실제 `USER_REVIEW.md` 작성을 소유한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `request-contract`: `/v1/chat/completions`가 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`을 허용하고 타입, 범위, 충돌 조합을 검증한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`
|
||||
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- `agent-ops/rules/project/rules.md`
|
||||
- `agent-ops/rules/common/rules-roadmap.md`
|
||||
- `agent-ops/rules/project/domain/edge/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-contract/index.md`
|
||||
- `agent-contract/outer/openai-compatible-api.md`
|
||||
- `apps/edge/internal/openai/types.go`
|
||||
- `apps/edge/internal/openai/chat_handler.go`
|
||||
- `apps/edge/internal/openai/server_test.go`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- 상태: `[승인됨]`, SDD 잠금: `해제`, 사용자 리뷰: 없음
|
||||
- Acceptance Scenario: S01 -> `request-contract`
|
||||
- Evidence Map: S01 requires Edge OpenAI request decoder/unit test and `Roadmap Completion` evidence for `request-contract`.
|
||||
- 이 plan은 S01만 완료 대상으로 삼는다. S02/S03/S05/S06은 sibling split에서 처리한다.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`
|
||||
- 읽은 env/profile: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`
|
||||
- 적용 명령: `go test -count=1 ./apps/edge/internal/openai`
|
||||
- 확인한 baseline: `go test -count=1 ./apps/edge/internal/openai ./apps/node/internal/adapters/openai_compat` PASS
|
||||
- 외부 runner preflight: 이 split은 현재 checkout의 Edge unit test만 필요하므로 없음.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- 새 request field 허용: 현재 `TestChatCompletionsRejectsUnsupportedFields`가 `think`를 400으로 기대한다. 허용/거부 경계 테스트가 필요하다.
|
||||
- 타입/범위/충돌: 현재 `thinking_token_budget`, `reasoning_effort`, `include_reasoning` 검증 테스트가 없다.
|
||||
- 기존 unknown provider wrapper 거부: `options`, `format`, `keep_alive` 400 회귀를 유지해야 한다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 먼저 적용했다.
|
||||
- shared task group: `agent-task/m-openai-compatible-think-control/`
|
||||
- siblings:
|
||||
- `01_edge_request_contract`: independent foundation, this plan.
|
||||
- `02+01_node_provider_mapping`: depends on 01 run input keys.
|
||||
- `03+01_edge_reasoning_visibility`: depends on 01 `include_reasoning` request field.
|
||||
- `04+01,02,03_contract_docs`: waits for implemented behavior before docs.
|
||||
- `05+01,02,03,04_dev_smoke`: waits for code/docs and external runtime.
|
||||
- predecessor completion: none required for `01_edge_request_contract`; active/archive complete logs for this task group were absent.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Node provider request body mapping is excluded; `02+01_node_provider_mapping` owns it.
|
||||
- `reasoning_content` response filtering is excluded; `03+01_edge_reasoning_visibility` owns it.
|
||||
- Contract/dev docs and external smoke are excluded to avoid documenting or smoking behavior before implementation.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G04`: public API decode/validation change with bounded Edge files and deterministic unit tests.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `chatCompletionRequest`에 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning` 필드를 추가하고 `/v1/chat/completions` decoder allowlist와 validation을 갱신한다. 검증: unknown provider wrapper는 계속 400, 허용 필드는 Edge request decoder/unit test 통과.
|
||||
- [ ] `runInput`이 validated think-control 값을 내부 input map에 보존하되, omitted field는 기존 provider default 동작을 유지하도록 어떤 key도 추가하지 않는다.
|
||||
- [ ] `apps/edge/internal/openai/server_test.go`에 허용 필드, 범위/충돌 실패, unknown wrapper 회귀 테스트를 추가/갱신한다.
|
||||
- [ ] `go test -count=1 ./apps/edge/internal/openai`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [API-1] Edge Request Decode And Validation
|
||||
|
||||
#### 문제
|
||||
|
||||
- [apps/edge/internal/openai/types.go](/config/workspace/iop/apps/edge/internal/openai/types.go:13)의 `chatCompletionRequest`에 think-control 필드가 없다.
|
||||
- [apps/edge/internal/openai/chat_handler.go](/config/workspace/iop/apps/edge/internal/openai/chat_handler.go:92)의 decoder allowlist가 새 필드를 모두 거부한다.
|
||||
- [apps/edge/internal/openai/server_test.go](/config/workspace/iop/apps/edge/internal/openai/server_test.go:2152)는 `think`를 unsupported로 기대한다.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// apps/edge/internal/openai/chat_handler.go:97
|
||||
for key := range raw {
|
||||
switch key {
|
||||
case "model", "messages", "stream", "metadata", "max_tokens", "max_completion_tokens", "temperature", "top_p", "presence_penalty", "frequency_penalty", "seed", "stop", "response_format", "tools", "tool_choice", "parallel_tool_calls", "stream_options", "store":
|
||||
default:
|
||||
return fmt.Errorf("%s is not supported for /v1/chat/completions", key)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
// apps/edge/internal/openai/chat_handler.go:97
|
||||
case "model", "messages", "stream", "metadata", "max_tokens", "max_completion_tokens", "temperature", "top_p", "presence_penalty", "frequency_penalty", "seed", "stop", "response_format", "tools", "tool_choice", "parallel_tool_calls", "stream_options", "store", "think", "reasoning_effort", "thinking_token_budget", "include_reasoning":
|
||||
```
|
||||
|
||||
Validation baseline:
|
||||
|
||||
- `thinking_token_budget` must be `>= 0`.
|
||||
- `reasoning_effort` allowed values: `none`, `low`, `medium`, `high`; empty string is invalid when present.
|
||||
- `think=false` conflicts with `reasoning_effort` values other than `none`.
|
||||
- `thinking_token_budget` conflicts with `think=false` or `reasoning_effort=none`.
|
||||
- `include_reasoning` is exposure-only and does not conflict with generation controls.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Add pointer fields to distinguish omitted from zero values.
|
||||
- Add `validateThinkControl(req)` from `decodeChatCompletionRequest` after existing sampling validation.
|
||||
- Add helper methods on `chatCompletionRequest` to copy present fields into `input`: `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`.
|
||||
- Keep strict-output `input["think"]=false` behavior unless request explicitly set `think=true`; explicit `think=true` should win only when validation allows it.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/openai/types.go`: request fields, run input copy, helper functions.
|
||||
- [ ] `apps/edge/internal/openai/chat_handler.go`: decoder allowlist and validation call.
|
||||
- [ ] `apps/edge/internal/openai/server_test.go`: request contract tests and unsupported wrapper regression.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- Add `TestChatCompletionsAcceptsThinkControlFields` asserting `fake.req.Input`.
|
||||
- Update `TestChatCompletionsRejectsUnsupportedFields` to remove `think` from unsupported cases while keeping `options`, `format`, `keep_alive`.
|
||||
- Add `TestChatCompletionsRejectsInvalidThinkControlFields` table for negative budget, bad effort, `think=false` + `high`, and `reasoning_effort=none` + budget.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/openai
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/edge/internal/openai/types.go` | API-1 |
|
||||
| `apps/edge/internal/openai/chat_handler.go` | API-1 |
|
||||
| `apps/edge/internal/openai/server_test.go` | API-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/openai
|
||||
```
|
||||
|
||||
Expected: PASS. Fresh execution is required; do not rely on Go test cache.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<!-- 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 | [ ] |
|
||||
| [API-2] Default Compatibility Regression | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `01_edge_request_contract` predecessor `complete.log`를 확인한 뒤 시작한다.
|
||||
- [ ] `openai_compat` adapter request body builder가 provider label을 받아 think-control input을 provider별 body로 매핑하도록 바꾼다. 검증: vLLM Qwen3, vLLM-MLX, Lemonade mock이 같은 요청 플래그에 대해 기대 request body를 받는다.
|
||||
- [ ] omitted think-control field가 `think`, `reasoning_effort`, `thinking_token_budget`, `chat_template_kwargs`를 만들지 않는 회귀 테스트를 추가한다. 검증: 기존 chat/tool/provider pool targeted tests가 통과한다.
|
||||
- [ ] `go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] active plan/review 파일을 `.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 파일 또는 정당한 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `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
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai
|
||||
(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?**
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
<!-- task=m-openai-compatible-think-control/02+01_node_provider_mapping plan=0 tag=API -->
|
||||
|
||||
# Plan - API Node Provider Mapping
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`01_edge_request_contract`의 `complete.log`가 있어야 이 plan을 구현한다. 구현 완료는 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지다. Milestone lock decision이 막으면 review stub의 `사용자 리뷰 요청`에만 기록하고, 직접 사용자에게 질문하거나 archive/complete 파일을 만들지 않는다.
|
||||
|
||||
## 배경
|
||||
|
||||
Edge가 공개 think-control 필드를 내부 input map으로 전달해도 Node adapter가 provider별 request body로 매핑하지 않으면 실제 generation 제어가 동작하지 않는다. 현재 `openai_compat` adapter는 `think`를 단순 top-level passthrough하고, provider label별 `chat_template_kwargs.enable_thinking` 매핑이 없다. 이 split은 vLLM, vLLM-MLX, Lemonade mock request body를 고정하고 omitted field 회귀를 같이 닫는다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 선택된 Milestone lock decision이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 환경/secret/provider 접근 실패는 사용자 리뷰가 아니라 검증 blocker 또는 follow-up이다.
|
||||
|
||||
## 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
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- `agent-ops/rules/project/domain/node/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`
|
||||
- `apps/node/internal/adapters/openai_compat/openai_compat.go`
|
||||
- `apps/node/internal/adapters/openai_compat/openai_compat_test.go`
|
||||
- `apps/edge/internal/openai/types.go`
|
||||
- `apps/edge/internal/openai/server_test.go`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- 상태: `[승인됨]`, SDD 잠금: `해제`, 사용자 리뷰: 없음
|
||||
- Acceptance Scenario: S02 -> `provider-mapping`, S04 -> `default-compat`
|
||||
- Evidence Map: S02 requires Node request body unit tests with provider mocks; S04 requires existing chat/tool/provider-pool regression evidence.
|
||||
- 이 plan은 provider body mock tests를 completion evidence로 삼는다. External three-provider smoke is excluded to `05+01,02,03,04_dev_smoke`.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`
|
||||
- 읽은 env/profile: `agent-test/local/rules.md`, `agent-test/local/node-smoke.md`, `agent-test/local/edge-smoke.md`
|
||||
- 적용 명령: `go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai`
|
||||
- baseline 확인: `go test -count=1 ./apps/edge/internal/openai ./apps/node/internal/adapters/openai_compat` PASS
|
||||
- 외부 provider preflight: 이 split은 mock HTTP server만 사용하므로 없음.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `think=false` provider mapping: current tests do not assert `chat_template_kwargs.enable_thinking=false` for vLLM/vLLM-MLX or top-level Lemonade mapping.
|
||||
- `think=true` and `reasoning_effort=none`: no provider body test.
|
||||
- Omitted field default compatibility: existing tests check generic options/tool passthrough but not absence of `think`/`chat_template_kwargs`.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none. If `buildRequestBody` signature changes, update its call at `apps/node/internal/adapters/openai_compat/openai_compat.go:131` and tests in `apps/node/internal/adapters/openai_compat/openai_compat_test.go`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- shared task group: `agent-task/m-openai-compatible-think-control/`
|
||||
- current subtask: `02+01_node_provider_mapping`
|
||||
- dependency from directory name: predecessor `01_edge_request_contract`
|
||||
- predecessor completion: missing in active and archive lookup. Implementation must wait for `agent-task/m-openai-compatible-think-control/01_edge_request_contract/complete.log` or matching archive complete log.
|
||||
- split reason: provider mapping touches Node adapter and mock provider semantics; Edge response visibility and docs have separate ownership/risk.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Edge request decoder is excluded except for running its targeted tests after dependency completion.
|
||||
- Edge response `reasoning_content` filtering is excluded to `03+01_edge_reasoning_visibility`.
|
||||
- Real dev-runtime provider validation is excluded to `05+01,02,03,04_dev_smoke`.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G05`: bounded Node adapter change, but provider-specific compatibility matrix and default-preservation tests raise risk.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `01_edge_request_contract` predecessor `complete.log`를 확인한 뒤 시작한다.
|
||||
- [ ] `openai_compat` adapter request body builder가 provider label을 받아 think-control input을 provider별 body로 매핑하도록 바꾼다. 검증: vLLM Qwen3, vLLM-MLX, Lemonade mock이 같은 요청 플래그에 대해 기대 request body를 받는다.
|
||||
- [ ] omitted think-control field가 `think`, `reasoning_effort`, `thinking_token_budget`, `chat_template_kwargs`를 만들지 않는 회귀 테스트를 추가한다. 검증: 기존 chat/tool/provider pool targeted tests가 통과한다.
|
||||
- [ ] `go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `02+01_node_provider_mapping` decodes to `depends_on=[01]`.
|
||||
- Do not implement until predecessor `01_edge_request_contract` has a `complete.log`.
|
||||
|
||||
### [API-1] Provider-Specific Think Body Mapping
|
||||
|
||||
#### 문제
|
||||
|
||||
- [apps/node/internal/adapters/openai_compat/openai_compat.go](/config/workspace/iop/apps/node/internal/adapters/openai_compat/openai_compat.go:36)의 adapter는 `provider` label을 갖고 있지만 body builder가 이를 사용하지 않는다.
|
||||
- [apps/node/internal/adapters/openai_compat/openai_compat.go](/config/workspace/iop/apps/node/internal/adapters/openai_compat/openai_compat.go:440)의 `buildRequestBody`는 `think`만 top-level passthrough한다.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// apps/node/internal/adapters/openai_compat/openai_compat.go:440
|
||||
func buildRequestBody(model string, messages []chatMessage, input map[string]any) map[string]any {
|
||||
body := make(map[string]any)
|
||||
...
|
||||
for _, key := range []string{"tools", "tool_choice", "format", "think", "keep_alive"} {
|
||||
if v, ok := input[key]; ok {
|
||||
body[key] = v
|
||||
}
|
||||
}
|
||||
body["model"] = model
|
||||
body["messages"] = messages
|
||||
body["stream"] = true
|
||||
return body
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
// apps/node/internal/adapters/openai_compat/openai_compat.go
|
||||
reqBody, err := a.buildRequestBody(model, messages, spec.Input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
Provider mapping baseline:
|
||||
|
||||
| Provider label | `think=false` / `reasoning_effort=none` | `think=true` | Budget/effort |
|
||||
|---|---|---|---|
|
||||
| `vllm`, `vllm-mlx` | `chat_template_kwargs.enable_thinking=false` | `chat_template_kwargs.enable_thinking=true` | place supported budget/effort under `chat_template_kwargs`; unsupported values return adapter error with `unsupported think control` |
|
||||
| `lemonade` | top-level `think=false` | top-level `think=true` | top-level `reasoning_effort` / `thinking_token_budget` if present |
|
||||
| empty/unknown | keep current top-level `think` passthrough for compatibility | same | top-level passthrough for explicit fields |
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Convert `buildRequestBody` into an adapter method or pass provider label explicitly.
|
||||
- Merge `chat_template_kwargs` only inside the adapter; do not expose raw wrapper at Edge.
|
||||
- Preserve `tools`, `tool_choice`, `format`, `keep_alive` existing behavior unless tests show a regression.
|
||||
- Return a clear adapter error before HTTP for explicitly unsupported provider/value combinations.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/node/internal/adapters/openai_compat/openai_compat.go`: provider-aware body builder and helper functions.
|
||||
- [ ] `apps/node/internal/adapters/openai_compat/openai_compat_test.go`: provider body assertions for vLLM, vLLM-MLX, Lemonade, omitted default.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- Add `TestOpenAICompatExecuteMapsThinkFalseForVLLMChatTemplateKwargs`.
|
||||
- Add `TestOpenAICompatExecuteMapsThinkFalseForVLLMMLXChatTemplateKwargs`.
|
||||
- Add `TestOpenAICompatExecuteMapsThinkFalseForLemonadeTopLevel`.
|
||||
- Add `TestOpenAICompatExecuteOmitsThinkControlWhenRequestOmitsIt`.
|
||||
- Add/extend tests for `reasoning_effort=none`, `think=true`, and explicit unsupported budget/effort behavior.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/node/internal/adapters/openai_compat
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### [API-2] Default Compatibility Regression
|
||||
|
||||
#### 문제
|
||||
|
||||
SDD S04 requires omitted think fields to preserve existing provider default thinking behavior. Current body tests do not assert that omitted requests avoid `think` and `chat_template_kwargs`.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- In the omitted-field test, decode the provider request body and assert absence of:
|
||||
- `think`
|
||||
- `reasoning_effort`
|
||||
- `thinking_token_budget`
|
||||
- `chat_template_kwargs`
|
||||
- Keep existing options/tool tests passing.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/node/internal/adapters/openai_compat/openai_compat_test.go`: absence assertions.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- Covered by `TestOpenAICompatExecuteOmitsThinkControlWhenRequestOmitsIt`.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/node/internal/adapters/openai_compat/openai_compat.go` | API-1 |
|
||||
| `apps/node/internal/adapters/openai_compat/openai_compat_test.go` | API-1, API-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/edge/internal/openai
|
||||
```
|
||||
|
||||
Expected: PASS. Fresh execution is required; do not rely on Go test cache.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<!-- task=m-openai-compatible-think-control/03+01_edge_reasoning_visibility plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> Start only after predecessor `01_edge_request_contract` has `complete.log`.
|
||||
> Fill all implementation-owned sections and verification output, then stop with active files in place. Finalization is review-agent-only.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-03
|
||||
task=m-openai-compatible-think-control/03+01_edge_reasoning_visibility, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `reasoning-visibility`: `include_reasoning=false`가 provider reasoning 생성 여부와 별개로 OpenAI-compatible 응답의 `reasoning_content` 노출을 억제한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 구현 에이전트는 종결 절차를 실행하지 않는다.
|
||||
|
||||
stream/non-stream suppression, default exposure regression, tool/content preservation, 검증 출력 일치를 확인한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Non-Streaming Reasoning Suppression | [ ] |
|
||||
| [API-2] Streaming Reasoning Suppression | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `01_edge_request_contract` predecessor `complete.log`를 확인한 뒤 시작한다.
|
||||
- [ ] non-streaming completion에서 `include_reasoning=false`이면 `message.reasoning_content`를 비우되 `content`, `tool_calls`, `finish_reason`, usage는 유지한다. 검증: non-stream 응답에서 reasoning field가 빠지고 content/tool output은 유지된다.
|
||||
- [ ] live SSE stream에서 `include_reasoning=false`이면 `delta.reasoning_content` chunk를 쓰지 않되 content/tool chunks와 `[DONE]`은 유지한다. 검증: stream 응답에서 reasoning delta가 빠지고 content/tool output은 유지된다.
|
||||
- [ ] default/omitted `include_reasoning`은 기존 reasoning exposure tests를 유지한다.
|
||||
- [ ] `go test -count=1 ./apps/edge/internal/openai`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] active plan/review 파일을 `.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 파일 또는 정당한 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `include_reasoning=false`가 response-only filter인지 확인한다.
|
||||
- content/tool output과 `[DONE]`이 suppression 중에도 유지되는지 확인한다.
|
||||
- omitted `include_reasoning`의 기존 reasoning exposure tests가 계속 통과하는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/openai
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/openai
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/openai
|
||||
(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?**
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
<!-- task=m-openai-compatible-think-control/03+01_edge_reasoning_visibility plan=0 tag=API -->
|
||||
|
||||
# Plan - API Edge Reasoning Visibility
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`01_edge_request_contract`의 `complete.log`가 있어야 이 plan을 구현한다. 구현 완료는 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 내용과 검증 출력으로 채운 상태다. review finalization, archive, `complete.log` 작성은 code-review 전용이다.
|
||||
|
||||
## 배경
|
||||
|
||||
SDD는 `include_reasoning=false`를 generation control이 아니라 OpenAI-compatible response exposure control로 정의했다. 현재 Edge는 non-streaming `message.reasoning_content`와 live SSE `delta.reasoning_content`를 기본으로 노출한다. 요청자가 reasoning 생성을 유지하면서 노출만 숨길 수 있도록 response writer 단에서 필터를 추가해야 한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
선택된 Milestone lock decision이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 외부 환경이나 검증 evidence 공백은 사용자 리뷰가 아니라 blocker/follow-up으로 기록한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `reasoning-visibility`: `include_reasoning=false`가 provider reasoning 생성 여부와 별개로 OpenAI-compatible 응답의 `reasoning_content` 노출을 억제한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.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/edge-smoke.md`
|
||||
- `apps/edge/internal/openai/types.go`
|
||||
- `apps/edge/internal/openai/chat_handler.go`
|
||||
- `apps/edge/internal/openai/stream.go`
|
||||
- `apps/edge/internal/openai/server_test.go`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- 상태: `[승인됨]`, SDD 잠금: `해제`, 사용자 리뷰: 없음
|
||||
- Acceptance Scenario: S03 -> `reasoning-visibility`
|
||||
- Evidence Map: S03 requires stream/non-stream response filtering tests and `Roadmap Completion` evidence for `reasoning-visibility`.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`
|
||||
- 읽은 env/profile: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`
|
||||
- 적용 명령: `go test -count=1 ./apps/edge/internal/openai`
|
||||
- baseline 확인: `go test -count=1 ./apps/edge/internal/openai ./apps/node/internal/adapters/openai_compat` PASS
|
||||
- 외부 runner preflight: 이 split은 current checkout unit test만 필요하므로 없음.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- Current tests assert default reasoning exposure:
|
||||
- non-streaming `TestChatCompletionsReturnsReasoningContentSeparately`
|
||||
- streaming `TestChatCompletionsStreamsReasoningSSE`
|
||||
- No tests assert `include_reasoning=false` suppression while preserving content/tool output.
|
||||
- Strict-output tests suppress reasoning for a different policy and must not be reused as this feature's evidence.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- current subtask: `03+01_edge_reasoning_visibility`
|
||||
- dependency from directory name: predecessor `01_edge_request_contract`
|
||||
- predecessor completion: missing in active and archive lookup. Implementation must wait for `01_edge_request_contract/complete.log`.
|
||||
- split reason: response filtering is Edge-only but depends on the request field introduced by 01; Node provider body mapping remains separate.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Request decoder validation is excluded to predecessor 01.
|
||||
- Provider-side generation control is excluded to sibling 02.
|
||||
- Contract docs and dev smoke are excluded to later dependent subtasks.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G04`: bounded Edge stream/non-stream filtering with deterministic tests, but SSE behavior and tool output preservation add moderate risk.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `01_edge_request_contract` predecessor `complete.log`를 확인한 뒤 시작한다.
|
||||
- [ ] non-streaming completion에서 `include_reasoning=false`이면 `message.reasoning_content`를 비우되 `content`, `tool_calls`, `finish_reason`, usage는 유지한다. 검증: non-stream 응답에서 reasoning field가 빠지고 content/tool output은 유지된다.
|
||||
- [ ] live SSE stream에서 `include_reasoning=false`이면 `delta.reasoning_content` chunk를 쓰지 않되 content/tool chunks와 `[DONE]`은 유지한다. 검증: stream 응답에서 reasoning delta가 빠지고 content/tool output은 유지된다.
|
||||
- [ ] default/omitted `include_reasoning`은 기존 reasoning exposure tests를 유지한다.
|
||||
- [ ] `go test -count=1 ./apps/edge/internal/openai`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `03+01_edge_reasoning_visibility` decodes to `depends_on=[01]`.
|
||||
- Do not implement until predecessor `01_edge_request_contract` has a `complete.log`.
|
||||
|
||||
### [API-1] Non-Streaming Reasoning Suppression
|
||||
|
||||
#### 문제
|
||||
|
||||
[apps/edge/internal/openai/chat_handler.go](/config/workspace/iop/apps/edge/internal/openai/chat_handler.go:249)의 collection path always builds `chatMessage{ReasoningContent: reasoning}` after normalization. No request-level visibility switch is applied.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// apps/edge/internal/openai/chat_handler.go:249
|
||||
text, reasoning, finishReason, nativeToolCalls, usage, textToolFallback, err := collectRunResult(ctx, handle.Stream(), handle.WaitTimeout())
|
||||
...
|
||||
message := chatMessage{Role: "assistant", Content: text, ReasoningContent: reasoning}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
// apply after normalization, before response serialization
|
||||
if !req.includeReasoning() {
|
||||
message.ReasoningContent = ""
|
||||
}
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Add a small helper such as `req.includeReasoning()` that defaults to true.
|
||||
- Filter only response exposure. Do not alter collected reasoning logs or provider generation input.
|
||||
- Ensure tool call synthesis still sees content and does not depend on reasoning.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/openai/types.go`: include helper if not already added by 01.
|
||||
- [ ] `apps/edge/internal/openai/chat_handler.go`: non-streaming response filter.
|
||||
- [ ] `apps/edge/internal/openai/server_test.go`: non-stream suppression tests.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- Add `TestChatCompletionsSuppressesReasoningContentWhenExcluded`.
|
||||
- Add a tool-call preserving case if existing native/text tool tests make this cheap; otherwise include content preservation in the suppression test.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/openai
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### [API-2] Streaming Reasoning Suppression
|
||||
|
||||
#### 문제
|
||||
|
||||
- [apps/edge/internal/openai/stream.go](/config/workspace/iop/apps/edge/internal/openai/stream.go:65) writes every reasoning delta to SSE.
|
||||
- [apps/edge/internal/openai/stream.go](/config/workspace/iop/apps/edge/internal/openai/stream.go:167) only suppresses reasoning for strict-output, not request visibility.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// apps/edge/internal/openai/stream.go:167
|
||||
case "reasoning_delta":
|
||||
if event.GetDelta() == "" {
|
||||
continue
|
||||
}
|
||||
reasoningBuilder.WriteString(event.GetDelta())
|
||||
if outputPolicy.Strict {
|
||||
continue
|
||||
}
|
||||
writeTracedReasoningDelta(event.GetDelta())
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
if outputPolicy.Strict || !req.includeReasoning() {
|
||||
continue
|
||||
}
|
||||
writeTracedReasoningDelta(event.GetDelta())
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Suppress live SSE reasoning chunks when `include_reasoning=false`.
|
||||
- Keep `reasoningBuilder` for logs and diagnostics.
|
||||
- Buffered strict stream currently emits only content/tool output; verify it still does not leak reasoning and add a non-strict live SSE test.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/internal/openai/stream.go`: live SSE reasoning filter.
|
||||
- [ ] `apps/edge/internal/openai/server_test.go`: streaming suppression tests.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- Add `TestChatCompletionsStreamSuppressesReasoningWhenExcluded` asserting no `reasoning_content`, content chunk present, `[DONE]` present.
|
||||
- Keep `TestChatCompletionsStreamsReasoningSSE` passing for omitted `include_reasoning`.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/openai
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/edge/internal/openai/types.go` | API-1 |
|
||||
| `apps/edge/internal/openai/chat_handler.go` | API-1 |
|
||||
| `apps/edge/internal/openai/stream.go` | API-2 |
|
||||
| `apps/edge/internal/openai/server_test.go` | API-1, API-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/internal/openai
|
||||
```
|
||||
|
||||
Expected: PASS. Fresh execution is required; do not rely on Go test cache.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<!-- task=m-openai-compatible-think-control/04+01,02,03_contract_docs plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> Start only after predecessors `01`, `02`, and `03` have `complete.log`.
|
||||
> Fill implementation-owned sections and verification output, then stop with active files in place. Finalization is review-agent-only.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-03
|
||||
task=m-openai-compatible-think-control/04+01,02,03_contract_docs, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `contract-docs`: OpenAI-compatible 계약 문서와 dev 운영 문서가 요청별 thinking/reasoning 제어 field, provider별 unsupported 정책, 기본값을 설명한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 구현 에이전트는 종결 절차를 실행하지 않는다.
|
||||
|
||||
계약 원문과 사람용 docs가 코드와 일치하는지, raw wrapper 금지가 유지되는지, secrets가 쓰이지 않았는지 확인한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] OpenAI-Compatible Contract Update | [ ] |
|
||||
| [API-2] Dev Operations Docs Update | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] Predecessor `complete.log` for `01`, `02`, and `03`를 확인한 뒤 시작한다.
|
||||
- [ ] `agent-contract/outer/openai-compatible-api.md`의 Chat Completions field list, field semantics, conflict/unsupported policy, response visibility policy, forbidden field list를 구현 결과와 일치시킨다.
|
||||
- [ ] `docs/edge-local-dev-guide.md`의 dev-runtime provider pool section에 request-level think-control smoke usage와 default/visibility caveat를 짧게 추가한다.
|
||||
- [ ] `docs/openai-compatible-api-contract.md`가 원문 포인터로 유지되는지 확인하고 계약 본문을 복제하지 않는다.
|
||||
- [ ] `rg --sort path -n "think|reasoning_effort|thinking_token_budget|include_reasoning|chat_template_kwargs" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md docs/openai-compatible-api-contract.md`를 실행해 문서 상태를 확인한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] active plan/review 파일을 `.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 파일 또는 정당한 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `agent-contract/outer/openai-compatible-api.md`가 source of truth이고 docs pointer가 계약 본문을 복제하지 않는지 확인한다.
|
||||
- `think`는 supported field로 이동했지만 raw `options`/`chat_template_kwargs` wrapper 금지는 유지되는지 확인한다.
|
||||
- dev guide에 secret/token 원문이 추가되지 않았는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ rg --sort path -n "think|reasoning_effort|thinking_token_budget|include_reasoning|chat_template_kwargs" agent-contract/outer/openai-compatible-api.md
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ rg --sort path -n "think=false|include_reasoning|reasoning_effort|thinking_token_budget" docs/edge-local-dev-guide.md docs/openai-compatible-api-contract.md
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ rg --sort path -n "think|reasoning_effort|thinking_token_budget|include_reasoning|chat_template_kwargs" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md docs/openai-compatible-api-contract.md
|
||||
(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?**
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
<!-- task=m-openai-compatible-think-control/04+01,02,03_contract_docs plan=0 tag=API -->
|
||||
|
||||
# Plan - API Contract Docs
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 plan은 `01_edge_request_contract`, `02+01_node_provider_mapping`, `03+01_edge_reasoning_visibility`가 PASS complete된 뒤 구현한다. 계약 문서를 실제 코드와 다르게 선반영하지 않는다. 구현 완료는 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운 상태이며, finalization은 code-review 전용이다.
|
||||
|
||||
## 배경
|
||||
|
||||
현재 계약 원문은 Chat Completions 지원 필드 목록에서 think-control 필드를 빼고, 금지 목록에서는 `think`를 provider 전용 field로 거부한다고 설명한다. 앞선 split들이 공개 field와 provider mapping, response visibility를 구현한 뒤 계약 원문과 dev 운영 문서에 그 결과를 맞춰야 한다. `docs/openai-compatible-api-contract.md`는 계약 원문 포인터이므로 원문 복제 없이 유지한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 선택된 Milestone lock decision이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 문서 문구 조정, evidence 공백, 외부 smoke 실패는 사용자 리뷰가 아니라 follow-up/review finding으로 처리한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `contract-docs`: OpenAI-compatible 계약 문서와 dev 운영 문서가 요청별 thinking/reasoning 제어 field, provider별 unsupported 정책, 기본값을 설명한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- `agent-contract/index.md`
|
||||
- `agent-contract/outer/openai-compatible-api.md`
|
||||
- `docs/openai-compatible-api-contract.md`
|
||||
- `docs/edge-local-dev-guide.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- 상태: `[승인됨]`, SDD 잠금: `해제`, 사용자 리뷰: 없음
|
||||
- Acceptance Scenario: S06 -> `contract-docs`
|
||||
- Evidence Map: S06 requires contract document diff and documentation verification.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `local`
|
||||
- 읽은 env/profile: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`
|
||||
- 적용 명령: deterministic docs search with `rg --sort path`, plus targeted Go tests from completed code splits if reviewer wants behavior/doc consistency.
|
||||
- 외부 runner preflight: none. This docs split must not run external dev-runtime smoke.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- Docs currently do not describe `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`.
|
||||
- Docs currently say `think` is forbidden for Chat Completions.
|
||||
- Dev guide mentions provider runtime `enable_thinking=false` but not request-level overrides or response exposure policy.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- current subtask: `04+01,02,03_contract_docs`
|
||||
- dependency from directory name: predecessors `01`, `02`, `03`
|
||||
- predecessor completion: all missing in active and archive lookup at plan creation.
|
||||
- split reason: docs must mirror finalized code behavior and unsupported policy; writing them before code would create false public guidance.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Code changes are excluded. If implementation finds docs cannot match code, stop and record a normal review/follow-up issue rather than inventing contract behavior.
|
||||
- `docs/openai-compatible-api-contract.md` should remain a pointer unless the project rule changes.
|
||||
- External dev smoke evidence is excluded to `05+01,02,03,04_dev_smoke`.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `local-G03`: docs-only after code completion, with contract source-of-truth discipline.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] Predecessor `complete.log` for `01`, `02`, and `03`를 확인한 뒤 시작한다.
|
||||
- [ ] `agent-contract/outer/openai-compatible-api.md`의 Chat Completions field list, field semantics, conflict/unsupported policy, response visibility policy, forbidden field list를 구현 결과와 일치시킨다.
|
||||
- [ ] `docs/edge-local-dev-guide.md`의 dev-runtime provider pool section에 request-level think-control smoke usage와 default/visibility caveat를 짧게 추가한다.
|
||||
- [ ] `docs/openai-compatible-api-contract.md`가 원문 포인터로 유지되는지 확인하고 계약 본문을 복제하지 않는다.
|
||||
- [ ] `rg --sort path -n "think|reasoning_effort|thinking_token_budget|include_reasoning|chat_template_kwargs" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md docs/openai-compatible-api-contract.md`를 실행해 문서 상태를 확인한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `04+01,02,03_contract_docs` decodes to `depends_on=[01,02,03]`.
|
||||
- Do not implement until all three predecessors have `complete.log`.
|
||||
- Contract update work should follow `agent-contract/index.md`; if the implementation turn triggers contract-update skill routing, read the matched skill before editing.
|
||||
|
||||
### [API-1] OpenAI-Compatible Contract Update
|
||||
|
||||
#### 문제
|
||||
|
||||
[agent-contract/outer/openai-compatible-api.md](/config/workspace/iop/agent-contract/outer/openai-compatible-api.md:148)의 supported field list lacks think-control fields, and [agent-contract/outer/openai-compatible-api.md](/config/workspace/iop/agent-contract/outer/openai-compatible-api.md:179) forbids `think`.
|
||||
|
||||
Before:
|
||||
|
||||
```markdown
|
||||
<!-- agent-contract/outer/openai-compatible-api.md:148 -->
|
||||
현재 지원하는 Chat Completions request field:
|
||||
...
|
||||
- `store`
|
||||
...
|
||||
- `options`, `think`, `format`, `keep_alive` 같은 provider/Ollama 전용 request field
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```markdown
|
||||
현재 지원하는 Chat Completions request field:
|
||||
...
|
||||
- `think`
|
||||
- `reasoning_effort`
|
||||
- `thinking_token_budget`
|
||||
- `include_reasoning`
|
||||
|
||||
Think 제어 field:
|
||||
- `think`: ...
|
||||
- `include_reasoning`: response exposure only ...
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Add field semantics from approved SDD without copying the whole SDD.
|
||||
- Describe default behavior: omitted fields preserve provider default.
|
||||
- Describe unsupported policy from implemented code: invalid conflicts are 400 at Edge; provider-specific unsupported mapping returns clear compatibility/run error according to implementation.
|
||||
- Keep raw `options` and raw `chat_template_kwargs` forbidden.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `agent-contract/outer/openai-compatible-api.md`: source-of-truth update.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- No code test. Use deterministic `rg` verification and review against completed code split behavior.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
rg --sort path -n "think|reasoning_effort|thinking_token_budget|include_reasoning|chat_template_kwargs" agent-contract/outer/openai-compatible-api.md
|
||||
```
|
||||
|
||||
Expected: supported fields and raw-wrapper prohibition are both visible.
|
||||
|
||||
### [API-2] Dev Operations Docs Update
|
||||
|
||||
#### 문제
|
||||
|
||||
[docs/edge-local-dev-guide.md](/config/workspace/iop/docs/edge-local-dev-guide.md:236) says Qwen models may include thinking/reasoning output but does not describe request-level control or visibility suppression.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Add a concise subsection near the dev-runtime provider pool smoke notes.
|
||||
- Include sample intent, not secrets: `think=false`, omitted default, `include_reasoning=false`.
|
||||
- Do not write bearer tokens or private endpoint secrets.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `docs/edge-local-dev-guide.md`: dev-runtime think-control note.
|
||||
- [ ] `docs/openai-compatible-api-contract.md`: confirm pointer-only state; edit only if link text becomes stale.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
- No code test. Use deterministic `rg` verification.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
rg --sort path -n "think=false|include_reasoning|reasoning_effort|thinking_token_budget" docs/edge-local-dev-guide.md docs/openai-compatible-api-contract.md
|
||||
```
|
||||
|
||||
Expected: dev guide contains request-level guidance; pointer doc does not duplicate contract body unless intentionally updated.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `agent-contract/outer/openai-compatible-api.md` | API-1 |
|
||||
| `docs/edge-local-dev-guide.md` | API-2 |
|
||||
| `docs/openai-compatible-api-contract.md` | API-2 verification only |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
rg --sort path -n "think|reasoning_effort|thinking_token_budget|include_reasoning|chat_template_kwargs" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md docs/openai-compatible-api-contract.md
|
||||
```
|
||||
|
||||
Expected: contract source describes supported public fields and keeps raw provider wrappers forbidden; dev guide references request-level smoke usage; pointer doc remains a pointer.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<!-- task=m-openai-compatible-think-control/05+01,02,03,04_dev_smoke plan=0 tag=TEST -->
|
||||
|
||||
# Code Review Reference - TEST
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> Start only after predecessors `01`, `02`, `03`, and `04` have `complete.log`.
|
||||
> Fill implementation-owned sections and verification output, then stop with active files in place. Finalization is review-agent-only.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-07-03
|
||||
task=m-openai-compatible-think-control/05+01,02,03,04_dev_smoke, plan=0, tag=TEST
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `dev-smoke`: 실제 dev provider pool에서 `think=false`, omitted default, `include_reasoning=false` 요청을 실행하고 provider raw trace와 Edge SSE를 비교한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 구현 에이전트는 종결 절차를 실행하지 않는다.
|
||||
|
||||
이 review는 외부 dev-runtime 증거의 신뢰성을 확인한다. source ref와 binary freshness, full test 결과, provider pool 연결 상태, capacity observation, 그리고 think-control raw trace/SSE 비교가 모두 있어야 PASS다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [TEST-1] Remote Runner Preflight | [ ] |
|
||||
| [TEST-2] Clean Sync, Full Test, Rebuild, Restart | [ ] |
|
||||
| [TEST-3] Think-Control SSE And Provider Trace | [ ] |
|
||||
| [TEST-4] Capacity Health Evidence | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] Predecessor `complete.log` for `01`, `02`, `03`, and `04`를 확인한 뒤 시작한다.
|
||||
- [ ] Remote runner inventory를 재확인하고 source ref, dirty state, branch, HEAD를 기록한다.
|
||||
- [ ] Clean sync 기준 ref를 확정한다. Milestone 구현 commit이 `origin/main`에 없으면 같은 source ref를 원격에 명시적으로 반영한 뒤 진행하고, 임의로 stale `origin/main`을 검증하지 않는다.
|
||||
- [ ] Remote runner에서 `go test ./...`를 빌드 전 실행한다. 실패하면 배포를 중단한다.
|
||||
- [ ] dev-runtime Edge/mac/Linux ARM64/Windows AMD64 Node binary를 같은 source ref에서 rebuild한다.
|
||||
- [ ] 빌드 후 `config check`, `config refresh --help`, `config refresh --mode dry-run`, `go test ./...`를 실행한다.
|
||||
- [ ] Edge, mac-codex-node, gx10-vllm-node, onexplayer-lemonade-node를 dev-runtime 절차대로 재시작하고 port/process/status를 확인한다.
|
||||
- [ ] `/v1/models`가 `qwen3.6:35b`를 노출하는지 확인한다.
|
||||
- [ ] `/v1/responses`와 `/v1/chat/completions` capacity smoke를 capacity+1 기준으로 실행하고 provider `in_flight/queued` snapshot을 남긴다.
|
||||
- [ ] `/v1/chat/completions`에서 omitted default, `think=false`, `include_reasoning=false` 세 케이스를 stream으로 실행하고 Edge SSE와 provider raw trace를 비교한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 실행 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] active plan/review 파일을 `.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
|
||||
- [ ] WARN/FAIL이면 다음 active plan/review 파일 또는 정당한 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 실행한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 실행/검증 결정 사항을 기록한다. Source ref, provider trace source, masking policy, and any skipped deployment step must be explicit._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 연결 대상: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- Source ref가 predecessor 구현을 포함하고, remote binary가 같은 ref에서 rebuild되었는지 확인한다.
|
||||
- Full `go test ./...` pre/post 결과가 있거나, 누락 사유와 위험이 명확한지 확인한다.
|
||||
- Raw trace/SSE evidence가 세 케이스 모두에 대해 있고, token/API key/private path가 마스킹되었는지 확인한다.
|
||||
- `include_reasoning=false`는 generation disable이 아니라 exposure disable로 판정했는지 확인한다.
|
||||
- Capacity smoke는 request success만이 아니라 provider snapshot의 `in_flight/queued` 관측을 포함하는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다. Secrets and tokens must be masked._
|
||||
|
||||
### TEST-1 중간 검증
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git status --short --branch && git log --oneline -1 && test -f build/dev-runtime/edge.yaml && test -x build/dev-runtime/bin/edge && test -x build/dev-runtime/bin/iop-node'
|
||||
(output)
|
||||
```
|
||||
|
||||
### TEST-2 중간 검증
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && go test ./...'
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status | jq ".edge_id, .nodes"'
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18083/v1/models | jq ".data[].id"'
|
||||
(output)
|
||||
```
|
||||
|
||||
### TEST-3 중간 검증
|
||||
```bash
|
||||
$ # omitted default SSE and raw trace summary
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ # think=false SSE and raw trace summary
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ # include_reasoning=false SSE and raw trace summary
|
||||
(output)
|
||||
```
|
||||
|
||||
### TEST-4 중간 검증
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status | jq ".. | objects | select(has(\"capacity\") and has(\"in_flight\") and has(\"queued\")) | {id, capacity, in_flight, queued, health, served_models}"'
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git status --short --branch && git log --oneline -1'
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && go test ./...'
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18083/v1/models | jq ".data[].id"'
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status | jq "."'
|
||||
(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?**
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
<!-- task=m-openai-compatible-think-control/05+01,02,03,04_dev_smoke plan=0 tag=TEST -->
|
||||
|
||||
# Plan - Dev Runtime Think-Control Smoke
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
이 plan은 `01_edge_request_contract`, `02+01_node_provider_mapping`, `03+01_edge_reasoning_visibility`, `04+01,02,03_contract_docs`가 PASS complete된 뒤 실행한다. 이 작업은 원격 dev-runtime과 실제 provider pool을 사용하는 smoke 검증이며, 구현 변경을 새로 만들지 않는다. 실패가 나오면 원인과 증거를 `CODE_REVIEW-*-G??.md`에 남기고 follow-up plan으로 넘긴다.
|
||||
|
||||
## 배경
|
||||
|
||||
Milestone의 `dev-smoke`는 로컬 단위 테스트만으로 닫을 수 없다. 요청별 `think=false`, omitted default, `include_reasoning=false`가 Edge SSE와 provider raw trace에서 일관되게 관측되어야 한다. dev-runtime 인벤토리는 runner, Edge, Node, provider capacity, model alias를 이미 정의하고 있으므로 이 split은 깨끗한 원격 source 기준으로 배포/재시작/관측 증거를 확보한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
사용자 리뷰 요청은 선택된 Milestone lock decision이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 원격 접속 실패, provider 미기동, secret 부재, trace 미관측, 용량 smoke 불충족은 사용자 결정이 아니라 환경 blocker 또는 follow-up evidence로 기록한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- Task ids:
|
||||
- `dev-smoke`: 실제 dev provider pool에서 `think=false`, omitted default, `include_reasoning=false` 요청을 실행하고 provider raw trace와 Edge SSE를 비교한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
|
||||
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-test/dev/rules.md`
|
||||
- `agent-test/dev/edge-smoke.md`
|
||||
- `agent-test/dev/node-smoke.md`
|
||||
- `agent-test/dev/platform-common-smoke.md`
|
||||
- `agent-test/dev/inventory.yaml`
|
||||
- `agent-ops/skills/project/dev-runtime-deploy/SKILL.md`
|
||||
|
||||
### SDD 기준
|
||||
|
||||
- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-think-control/SDD.md`
|
||||
- 상태: `[승인됨]`, SDD 잠금: `해제`, 사용자 리뷰: 없음
|
||||
- Acceptance Scenario: S05 -> `dev-smoke`
|
||||
- Evidence Map: S05 requires dev smoke logs that compare provider raw trace and Edge SSE behavior for `think=false`, omitted default, and `include_reasoning=false`.
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- test_env: `dev`
|
||||
- profile: `dev-runtime-provider-pool`
|
||||
- remote runner: `ssh toki@toki-labs.com`
|
||||
- repo root: `/Users/toki/agent-work/iop-dev`
|
||||
- Edge id: `edge-toki-labs-dev`
|
||||
- Edge config: `build/dev-runtime/edge.yaml`
|
||||
- OpenAI base URL on runner: `http://127.0.0.1:18083/v1`
|
||||
- Control Plane status URL: `http://127.0.0.1:18001/edges/edge-toki-labs-dev/status`
|
||||
- admin addr on runner: `127.0.0.1:19093`
|
||||
- model alias: `qwen3.6:35b`
|
||||
- provider capacity total: `10`
|
||||
- dev-runtime-deploy skill applies. It requires clean sync, full test, full rebuild, restart, `/v1/models`, and OpenAI-compatible smoke evidence.
|
||||
- Secrets, bearer tokens, bootstrap tokens, and private key paths must not be written to final report or review output.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- Local tests can prove request decoding, adapter body mapping, and response filtering, but cannot prove actual vLLM/vLLM-MLX/Lemonade runtime behavior.
|
||||
- Provider request-body trace availability is environment-dependent. If raw request body cannot be observed, record the missing trace source and do not claim S05 PASS.
|
||||
- Omitted default can be nondeterministic if the model produces no reasoning on a given prompt. Use a reasoning-friendly prompt and record the result as observed behavior rather than exact output matching.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none. This split should not edit source symbols.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- current subtask: `05+01,02,03,04_dev_smoke`
|
||||
- dependency from directory name: predecessors `01`, `02`, `03`, `04`
|
||||
- predecessor completion: all missing in active and archive lookup at plan creation.
|
||||
- split reason: external dev-runtime deployment and provider evidence are slower, environment-sensitive, and should run only after code/docs splits are complete.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Code and docs changes are excluded. If smoke fails because implementation behavior is wrong, create a follow-up plan after review; do not hot-edit inside this smoke split.
|
||||
- Capacity smoke from `dev-runtime-deploy` is included only as deployment health evidence. The primary milestone evidence is think-control trace/SSE comparison.
|
||||
- OneXPlayer access must use `ssh r0bin@192.168.0.59` from the current host if it is needed. Do not use `toki` for OneXPlayer.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- `cloud-G07`: external runner, clean sync/rebuild/restart, multiple provider runtimes, raw trace collection, and SSE comparison.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] Predecessor `complete.log` for `01`, `02`, `03`, and `04`를 확인한 뒤 시작한다.
|
||||
- [ ] Remote runner inventory를 재확인하고 source ref, dirty state, branch, HEAD를 기록한다.
|
||||
- [ ] Clean sync 기준 ref를 확정한다. Milestone 구현 commit이 `origin/main`에 없으면 같은 source ref를 원격에 명시적으로 반영한 뒤 진행하고, 임의로 stale `origin/main`을 검증하지 않는다.
|
||||
- [ ] Remote runner에서 `go test ./...`를 빌드 전 실행한다. 실패하면 배포를 중단한다.
|
||||
- [ ] dev-runtime Edge/mac/Linux ARM64/Windows AMD64 Node binary를 같은 source ref에서 rebuild한다.
|
||||
- [ ] 빌드 후 `config check`, `config refresh --help`, `config refresh --mode dry-run`, `go test ./...`를 실행한다.
|
||||
- [ ] Edge, mac-codex-node, gx10-vllm-node, onexplayer-lemonade-node를 dev-runtime 절차대로 재시작하고 port/process/status를 확인한다.
|
||||
- [ ] `/v1/models`가 `qwen3.6:35b`를 노출하는지 확인한다.
|
||||
- [ ] `/v1/responses`와 `/v1/chat/completions` capacity smoke를 capacity+1 기준으로 실행하고 provider `in_flight/queued` snapshot을 남긴다.
|
||||
- [ ] `/v1/chat/completions`에서 omitted default, `think=false`, `include_reasoning=false` 세 케이스를 stream으로 실행하고 Edge SSE와 provider raw trace를 비교한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 실행 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- `05+01,02,03,04_dev_smoke` decodes to `depends_on=[01,02,03,04]`.
|
||||
- Do not execute remote smoke until all predecessors have `complete.log`.
|
||||
- If predecessors are archived, read only the matching `complete.log` evidence needed to prove dependency completion.
|
||||
|
||||
### [TEST-1] Remote Runner Preflight
|
||||
|
||||
#### 문제
|
||||
|
||||
dev-runtime smoke can produce misleading results if the remote checkout or binaries are stale.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Confirm remote checkout source and clean state before deployment.
|
||||
- Confirm expected binaries/config paths from inventory.
|
||||
- Record versions/timestamps without printing secrets.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] No source files.
|
||||
- [ ] `CODE_REVIEW-*-G??.md`: record preflight output.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
No automated test file. Use remote shell preflight.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git status --short --branch && git log --oneline -1 && test -f build/dev-runtime/edge.yaml && test -x build/dev-runtime/bin/edge && test -x build/dev-runtime/bin/iop-node'
|
||||
```
|
||||
|
||||
Expected: clean source for the intended ref, Edge config present, local Edge/mac Node binaries executable.
|
||||
|
||||
### [TEST-2] Clean Sync, Full Test, Rebuild, Restart
|
||||
|
||||
#### 문제
|
||||
|
||||
The smoke must validate the completed implementation, not an old binary or dirty remote tree.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Follow `agent-ops/skills/project/dev-runtime-deploy/SKILL.md`.
|
||||
- Clean sync only after confirming the target ref contains the milestone implementation.
|
||||
- Run pre-build and post-build `go test ./...`.
|
||||
- Rebuild all dev-runtime binaries from the same source ref.
|
||||
- Restart Edge and all dev provider nodes according to inventory.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] No source files.
|
||||
- [ ] `CODE_REVIEW-*-G??.md`: record source ref, test result, build artifacts, restart status.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
No repo test additions. This is deployment verification.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && go test ./...'
|
||||
ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status | jq ".edge_id, .nodes"'
|
||||
ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18083/v1/models | jq ".data[].id"'
|
||||
```
|
||||
|
||||
Expected: tests pass, Edge and three dev nodes are connected, `qwen3.6:35b` appears in models.
|
||||
|
||||
### [TEST-3] Think-Control SSE And Provider Trace
|
||||
|
||||
#### 문제
|
||||
|
||||
Milestone S05 requires real provider evidence for request-level think control and response exposure filtering.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Use `/v1/chat/completions` with `stream=true`, model `qwen3.6:35b`, and a reasoning-friendly prompt.
|
||||
- Send these cases:
|
||||
- omitted default: no think-control fields.
|
||||
- generation disabled: `think=false`.
|
||||
- visibility disabled: `include_reasoning=false`.
|
||||
- Include trace metadata if supported by the implemented Edge/Node path. Do not print API keys.
|
||||
- Save temporary SSE outputs and provider raw trace snippets under `/tmp` on the runner, then paste only relevant masked excerpts into `CODE_REVIEW-*-G??.md`.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] No source files.
|
||||
- [ ] `CODE_REVIEW-*-G??.md`: record each request body summary, Edge SSE observation, provider raw trace observation, and pass/fail judgment.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
No committed test file. Use a temporary runner script or direct `curl` commands.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && \
|
||||
BASE=http://127.0.0.1:18083/v1 MODEL=qwen3.6:35b \
|
||||
bash -lc '\''for case_name in omitted_default think_false include_reasoning_false; do echo "case=$case_name"; done'\'''
|
||||
```
|
||||
|
||||
Replace the placeholder loop with actual masked `curl -N` requests:
|
||||
|
||||
```json
|
||||
{"model":"qwen3.6:35b","stream":true,"messages":[{"role":"user","content":"Solve a two-step logic puzzle and explain the answer briefly."}]}
|
||||
{"model":"qwen3.6:35b","stream":true,"think":false,"messages":[{"role":"user","content":"Solve a two-step logic puzzle and explain the answer briefly."}]}
|
||||
{"model":"qwen3.6:35b","stream":true,"include_reasoning":false,"messages":[{"role":"user","content":"Solve a two-step logic puzzle and explain the answer briefly."}]}
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- omitted default preserves provider default. If reasoning appears, Edge SSE may expose `reasoning_content` according to implemented default visibility.
|
||||
- `think=false` provider raw request shows disabled thinking mapping for the selected provider path, and Edge SSE does not expose generated reasoning chunks for that request.
|
||||
- `include_reasoning=false` may still allow provider-side reasoning generation, but Edge SSE must not include `reasoning_content` or equivalent reasoning deltas.
|
||||
- All cases finish with valid content/tool/DONE stream behavior and no secret output.
|
||||
|
||||
### [TEST-4] Capacity Health Evidence
|
||||
|
||||
#### 문제
|
||||
|
||||
Think-control smoke should not pass while the dev provider pool is partially disconnected.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
- Run capacity+1 smoke for `/v1/responses` and `/v1/chat/completions`.
|
||||
- Poll Control Plane status while requests are in flight.
|
||||
- Record max aggregate `in_flight`, `queued`, and per-provider capacity observations.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] No source files.
|
||||
- [ ] `CODE_REVIEW-*-G??.md`: record max in-flight/queued and recovery snapshot.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
No committed test file. Use temporary runner script if needed.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
```bash
|
||||
ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status | jq ".. | objects | select(has(\"capacity\") and has(\"in_flight\") and has(\"queued\")) | {id, capacity, in_flight, queued, health, served_models}"'
|
||||
```
|
||||
|
||||
Expected: total capacity reaches `10`, queued reaches at least `1` during capacity+1 smoke, and all providers recover to `in_flight=0`, `queued=0` afterward.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| none | external dev-runtime smoke only |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git status --short --branch && git log --oneline -1'
|
||||
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && go test ./...'
|
||||
ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18083/v1/models | jq ".data[].id"'
|
||||
ssh toki@toki-labs.com 'curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status | jq "."'
|
||||
```
|
||||
|
||||
Expected: intended source ref, full tests pass, model exposed, dev provider pool connected, capacity health evidence captured, and think-control raw trace/SSE comparison recorded for all three cases.
|
||||
|
||||
모든 검증 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -96,7 +96,7 @@ func decodeChatCompletionRequest(dec *json.Decoder, req *chatCompletionRequest)
|
|||
}
|
||||
for key := range raw {
|
||||
switch key {
|
||||
case "model", "messages", "stream", "metadata", "max_tokens", "max_completion_tokens", "temperature", "top_p", "presence_penalty", "frequency_penalty", "seed", "stop", "response_format", "tools", "tool_choice", "parallel_tool_calls", "stream_options", "store":
|
||||
case "model", "messages", "stream", "metadata", "max_tokens", "max_completion_tokens", "temperature", "top_p", "presence_penalty", "frequency_penalty", "seed", "stop", "response_format", "tools", "tool_choice", "parallel_tool_calls", "stream_options", "store", "think", "reasoning_effort", "thinking_token_budget", "include_reasoning":
|
||||
default:
|
||||
return fmt.Errorf("%s is not supported for /v1/chat/completions", key)
|
||||
}
|
||||
|
|
@ -120,6 +120,38 @@ func decodeChatCompletionRequest(dec *json.Decoder, req *chatCompletionRequest)
|
|||
if req.TopP != nil && (*req.TopP < 0 || *req.TopP > 1) {
|
||||
return fmt.Errorf("top_p must be between 0 and 1")
|
||||
}
|
||||
if err := validateThinkControl(req); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateThinkControl(req *chatCompletionRequest) error {
|
||||
if req.ThinkingTokenBudget != nil && *req.ThinkingTokenBudget < 0 {
|
||||
return fmt.Errorf("thinking_token_budget must be non-negative")
|
||||
}
|
||||
if req.ReasoningEffort != nil {
|
||||
eff := *req.ReasoningEffort
|
||||
if eff == "" {
|
||||
return fmt.Errorf("reasoning_effort cannot be empty when present")
|
||||
}
|
||||
if eff != "none" && eff != "low" && eff != "medium" && eff != "high" {
|
||||
return fmt.Errorf("reasoning_effort must be one of none, low, medium, or high")
|
||||
}
|
||||
}
|
||||
if req.Think != nil && !*req.Think {
|
||||
if req.ReasoningEffort != nil && *req.ReasoningEffort != "none" {
|
||||
return fmt.Errorf("think=false conflicts with reasoning_effort=%s", *req.ReasoningEffort)
|
||||
}
|
||||
}
|
||||
if req.ThinkingTokenBudget != nil {
|
||||
if req.Think != nil && !*req.Think {
|
||||
return fmt.Errorf("thinking_token_budget cannot be set when think=false")
|
||||
}
|
||||
if req.ReasoningEffort != nil && *req.ReasoningEffort == "none" {
|
||||
return fmt.Errorf("thinking_token_budget cannot be set when reasoning_effort=none")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2156,7 +2156,6 @@ func TestChatCompletionsRejectsUnsupportedFields(t *testing.T) {
|
|||
body string
|
||||
}{
|
||||
{"options wrapper", `{"model":"m","messages":[{"role":"user","content":"hi"}],"options":{"num_ctx":8192}}`},
|
||||
{"think", `{"model":"m","messages":[{"role":"user","content":"hi"}],"think":false}`},
|
||||
{"format", `{"model":"m","messages":[{"role":"user","content":"hi"}],"format":"json"}`},
|
||||
{"keep_alive", `{"model":"m","messages":[{"role":"user","content":"hi"}],"keep_alive":"10m"}`},
|
||||
{"bad max_tokens", `{"model":"m","messages":[{"role":"user","content":"hi"}],"max_tokens":0}`},
|
||||
|
|
@ -2174,6 +2173,105 @@ func TestChatCompletionsRejectsUnsupportedFields(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestChatCompletionsAcceptsThinkControlFields(t *testing.T) {
|
||||
fake := &fakeRunService{
|
||||
events: make(chan *iop.RunEvent, 1),
|
||||
}
|
||||
fake.events <- &iop.RunEvent{Type: "complete"}
|
||||
|
||||
srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
|
||||
|
||||
// Case 1: All valid think-control fields provided
|
||||
reqBody := `{
|
||||
"model":"client-model",
|
||||
"messages":[{"role":"user","content":"hi"}],
|
||||
"think": true,
|
||||
"reasoning_effort": "medium",
|
||||
"thinking_token_budget": 1024,
|
||||
"include_reasoning": false
|
||||
}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(reqBody))
|
||||
w := httptest.NewRecorder()
|
||||
srv.routes().ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
if fake.req.Input["think"] != true {
|
||||
t.Fatalf("think: got %v, want true", fake.req.Input["think"])
|
||||
}
|
||||
if fake.req.Input["reasoning_effort"] != "medium" {
|
||||
t.Fatalf("reasoning_effort: got %v, want 'medium'", fake.req.Input["reasoning_effort"])
|
||||
}
|
||||
|
||||
budgetVal := fake.req.Input["thinking_token_budget"]
|
||||
switch v := budgetVal.(type) {
|
||||
case float64:
|
||||
if v != 1024 {
|
||||
t.Fatalf("thinking_token_budget: got %f, want 1024", v)
|
||||
}
|
||||
case int:
|
||||
if v != 1024 {
|
||||
t.Fatalf("thinking_token_budget: got %d, want 1024", v)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("thinking_token_budget: got %T, want number type", budgetVal)
|
||||
}
|
||||
|
||||
if fake.req.Input["include_reasoning"] != false {
|
||||
t.Fatalf("include_reasoning: got %v, want false", fake.req.Input["include_reasoning"])
|
||||
}
|
||||
|
||||
// Case 2: Omitted think-control fields should NOT add keys to runInput
|
||||
fake2 := &fakeRunService{
|
||||
events: make(chan *iop.RunEvent, 1),
|
||||
}
|
||||
fake2.events <- &iop.RunEvent{Type: "complete"}
|
||||
srv2 := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake2, nil)
|
||||
|
||||
reqBody2 := `{
|
||||
"model":"client-model",
|
||||
"messages":[{"role":"user","content":"hi"}]
|
||||
}`
|
||||
req2 := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(reqBody2))
|
||||
w2 := httptest.NewRecorder()
|
||||
srv2.routes().ServeHTTP(w2, req2)
|
||||
|
||||
if w2.Code != http.StatusOK {
|
||||
t.Fatalf("status: got %d body=%s", w2.Code, w2.Body.String())
|
||||
}
|
||||
for _, key := range []string{"think", "reasoning_effort", "thinking_token_budget", "include_reasoning"} {
|
||||
if _, ok := fake2.req.Input[key]; ok {
|
||||
t.Fatalf("expected key %q to be omitted from runInput, but it was present", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatCompletionsRejectsInvalidThinkControlFields(t *testing.T) {
|
||||
srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, &fakeRunService{}, nil)
|
||||
cases := []struct {
|
||||
name string
|
||||
body string
|
||||
}{
|
||||
{"negative budget", `{"model":"m","messages":[{"role":"user","content":"hi"}],"thinking_token_budget":-1}`},
|
||||
{"bad effort", `{"model":"m","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"super_high"}`},
|
||||
{"empty effort", `{"model":"m","messages":[{"role":"user","content":"hi"}],"reasoning_effort":""}`},
|
||||
{"think=false and reasoning_effort other than none", `{"model":"m","messages":[{"role":"user","content":"hi"}],"think":false,"reasoning_effort":"high"}`},
|
||||
{"thinking_token_budget conflicts with think=false", `{"model":"m","messages":[{"role":"user","content":"hi"}],"think":false,"thinking_token_budget":100}`},
|
||||
{"thinking_token_budget conflicts with reasoning_effort=none", `{"model":"m","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none","thinking_token_budget":100}`},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(tc.body))
|
||||
w := httptest.NewRecorder()
|
||||
srv.routes().ServeHTTP(w, req)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("got %d want 400, body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatCompletionsRejectsSourceMetadata(t *testing.T) {
|
||||
srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, &fakeRunService{}, nil)
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ type chatCompletionRequest struct {
|
|||
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"`
|
||||
StreamOptions any `json:"stream_options,omitempty"`
|
||||
Store any `json:"store,omitempty"`
|
||||
Think *bool `json:"think,omitempty"`
|
||||
ReasoningEffort *string `json:"reasoning_effort,omitempty"`
|
||||
ThinkingTokenBudget *int `json:"thinking_token_budget,omitempty"`
|
||||
IncludeReasoning *bool `json:"include_reasoning,omitempty"`
|
||||
}
|
||||
|
||||
type chatMessage struct {
|
||||
|
|
@ -90,7 +94,22 @@ func (req chatCompletionRequest) runInput(prompt string, messages []chatMessage,
|
|||
input["options"] = options
|
||||
}
|
||||
if strictOutput {
|
||||
input["think"] = false
|
||||
if req.Think != nil && *req.Think {
|
||||
input["think"] = true
|
||||
} else {
|
||||
input["think"] = false
|
||||
}
|
||||
} else if req.Think != nil {
|
||||
input["think"] = *req.Think
|
||||
}
|
||||
if req.ReasoningEffort != nil {
|
||||
input["reasoning_effort"] = *req.ReasoningEffort
|
||||
}
|
||||
if req.ThinkingTokenBudget != nil {
|
||||
input["thinking_token_budget"] = *req.ThinkingTokenBudget
|
||||
}
|
||||
if req.IncludeReasoning != nil {
|
||||
input["include_reasoning"] = *req.IncludeReasoning
|
||||
}
|
||||
if len(req.Tools) > 0 {
|
||||
input["tools"] = req.Tools
|
||||
|
|
|
|||
Loading…
Reference in a new issue