# Milestone: OpenAI Responses Input Surface

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`

## 목표

Edge OpenAI-compatible 입력 표면을 `/v1/chat/completions` baseline에서 non-streaming `POST /v1/responses`까지 확장한다. OpenAI-compatible Responses request shape를 받아 기존 Edge service와 Node execution 경로로 수렴시키고, IOP 공통 실행 힌트와 requester별 문맥은 별도 `iop` wrapper가 아니라 `metadata` field 아래의 최소 namespace로 보존한다.

## 상태

[완료]

## 구현 잠금

- 상태: 해제
- 결정 필요: 없음

## 범위

- Edge OpenAI-compatible HTTP server에 non-streaming `POST /v1/responses` 표면 추가
- NomadCode Core가 보내는 `model`, string `input`, optional `instructions`, optional structured `metadata`, `stream=false` 요청 shape 지원
- Responses request를 기존 Edge service `SubmitRun` 경로의 `adapter + target` 실행으로 변환
- request `metadata`를 IOP 공통 root와 requester namespace 기준으로 service boundary까지 전달하고, `metadata.inference.target`의 internal target override 정책을 테스트로 고정
- NomadCode Core parser가 읽을 수 있는 Responses-compatible response subset 제공
- `iop-edge smoke openai` 또는 동등한 command/smoke에서 `/v1/responses` non-streaming path 검증

## 기능

### Epic: [responses-surface] Responses API Input Surface

- [x] [responses-handler] Edge OpenAI-compatible server가 `POST /v1/responses` non-streaming 요청을 받고 기존 service execution 경로로 변환한다. 검증: `go test -count=1 ./apps/edge/internal/openai`
- [x] [metadata-contract] request `metadata`가 `request_id`, `inference.target`, `nomadcode.task_id`, `nomadcode.source` 최소 계약으로 Edge service boundary까지 보존되고, `metadata.inference.target` target override 정책이 테스트로 고정된다. 검증: `go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service`
- [x] [response-shape] Responses 응답이 `id`, `model`, `output_text` 또는 `output[].content[].text`, `usage`를 제공해 NomadCode Core parser와 호환된다. 검증: OpenAI server tests가 NomadCode-compatible response subset을 assert한다.
- [x] [responses-smoke] `iop-edge smoke openai` 또는 동등 smoke가 `/healthz`, `/v1/models`, `POST /v1/responses`를 확인한다. 검증: remote/local edge smoke에서 non-streaming Responses path가 실제 listener에 대해 PASS한다.

## Metadata V1 계약

Responses API root field인 `model`, `input`, `instructions`, `stream`은 OpenAI-compatible request shape로 유지한다. `metadata`는 IOP 공통 실행 힌트와 requester별 문맥을 담는 object이며, 현재 Milestone에서는 아래 최소 필드만 공식 지원한다.

```json
{
  "model": "qwen3.6:35b-a3b-bf16",
  "input": "Do the task...",
  "instructions": "Be concise.",
  "stream": false,
  "metadata": {
    "request_id": "req-abc",
    "inference": {
      "target": "qwen3.6:35b-a3b-bf16"
    },
    "nomadcode": {
      "task_id": "task-123",
      "source": "plane"
    }
  }
}
```

### 지원 필드

| field | 필수 | 의미 |
|-------|------|------|
| `metadata.request_id` | 선택 | 호출자와 IOP 로그를 묶는 correlation id |
| `metadata.inference.target` | 선택 | OpenAI-compatible `model`과 별도로 IOP 내부 inference target을 지정해야 할 때 쓰는 override |
| `metadata.nomadcode.task_id` | 선택 | NomadCode가 자기 task와 응답을 연결하기 위한 requester namespace 값 |
| `metadata.nomadcode.source` | 선택 | NomadCode 내부 task 출처. 예: `manual`, `plane`, `jira` |

### Target 결정 규칙

- internal target 우선순위: `config.openai.target` > `metadata.inference.target` > request root `model`
- response `model` 우선순위: request root `model` > internal target
- `/v1/responses`는 inference 표면이므로 `metadata.cli`는 현재 Milestone에서 지원하지 않는다.
- `metadata.inference`와 `metadata.cli`가 동시에 오거나, `metadata.cli`만 오면 `400 invalid_request_error`로 거절한다.

### 제외

- `metadata.route`, `metadata.routing`
- `metadata.execution`, `metadata.policy`, `metadata.requester`
- `metadata.schema`, `metadata.idempotency_key`
- `metadata.nomadcode.external`, `metadata.nomadcode.project`
- credential/token/raw secret/full prompt/local path

## 완료 리뷰

- 상태: 승인됨
- 요청일: 2026-06-08
- 완료 근거:
  - `agent-task/archive/2026/06/m-openai-responses-input-surface/01_responses_api_surface/complete.log`가 `responses-handler`, `metadata-contract`, `response-shape` PASS와 Edge package regression PASS를 기록했다.
  - `agent-task/archive/2026/06/m-openai-responses-input-surface/02+01_responses_smoke/complete.log`가 `responses-smoke` PASS와 `./scripts/e2e-openai-ollama.sh` 실제 Edge/Node/fake Ollama Responses smoke PASS를 기록했다.
  - workspace lock `nomadcode:external-integration`의 IOP Responses 선행 조건은 이 Milestone의 `[검토중]` 전환에 맞춰 enable로 동기화했다.
- 리뷰 필요:
  - [x] 사용자가 완료 결과를 확인했다
  - [x] archive 이동을 승인했다
- 리뷰 코멘트: 2026-06-08 전체 검토에서 smoke command 계약 drift를 정리하고 `go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/edge/cmd/edge ./apps/edge/internal/edgecmd`, `go test -count=1 ./apps/edge/...`, drift 검색, `./scripts/e2e-openai-ollama.sh`가 모두 PASS했다. 사용자 요청에 따라 완료로 전환하고 archive한다.

## 범위 제외

- streaming Responses API
- OpenAI Responses API 전체 schema 호환
- A2A task delegation 구현
- IOP native protocol로 NomadCode Core 실행 호출을 대체하는 작업
- NomadCode Core task create/enqueue/poll smoke 자체 구현. 해당 통합 smoke는 `../nomadcode`의 External Integration Milestone에서 수행한다.
- `metadata.cli` 기반 CLI 실행 라우팅

## 작업 컨텍스트

- 관련 경로: `apps/edge/internal/openai`, `apps/edge/internal/service`, `apps/edge/cmd/edge`, `agent-test/local/edge-smoke.md`
- 최우선 사유: `../nomadcode` External Integration의 `iop-responses` 완료와 archive를 해제하는 선행 Milestone이다.
- 표준선(선택): 외부 호출 표면은 OpenAI-compatible shape를 유지하고, IOP 공통 힌트는 `metadata` root에, requester-specific 문맥은 `metadata.<requester>` namespace 아래에 둔다.
- 선행 작업: Ollama 서빙 안정화 기반, Edge OpenAI-compatible chat completions baseline
- 후속 작업: `../nomadcode` External Integration의 Core -> IOP Responses enqueue smoke
- 외부 의존 잠금: `nomadcode:agent-roadmap/phase/external-integration/milestones/external-integration.md`는 이 Milestone이 `[검토중]` 또는 `[완료]`가 되어 workspace lock이 enable로 동기화되기 전까지 완료/archive할 수 없다.
- 확인 필요: 없음
