From f2dc60b93431b01dcb87d2ebd512f9fade0ec4a1 Mon Sep 17 00:00:00 2001 From: toki Date: Sat, 13 Jun 2026 11:33:53 +0900 Subject: [PATCH] chore: update docs and add contract documents - Update README.md and apps/edge/README.md - Add agent-contract/ directory with API contracts - Add docs/openai-compatible-api-contract.md - Update blackbox test file --- README.md | 4 +- agent-contract/index.md | 20 +++++ .../provided/openai-compatible-api.md | 79 +++++++++++++++++++ apps/edge/README.md | 2 + .../cli/opencode_sse_blackbox_test.go | 6 +- docs/openai-compatible-api-contract.md | 7 ++ 6 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 agent-contract/index.md create mode 100644 agent-contract/provided/openai-compatible-api.md create mode 100644 docs/openai-compatible-api-contract.md diff --git a/README.md b/README.md index 0e08cd7..4794564 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ adapter = cli target = codex-local ``` -외부 OpenAI API 호환 계층에서는 호환성을 위해 `model` 필드가 남을 수 있다. 그러나 내부 실행 개념에서는 모델 이름만으로 전체 실행을 설명하지 않고, `adapter`, `target`, `execution`, `node adapter`, `adapter execution` 같은 용어를 우선한다. IOP의 외부 실행 호출 계약은 OpenAI-compatible API 방식을 기본 표면으로 채택하되, IOP 고유의 workspace, session, agent, approval, artifact, notification 의미는 별도 `iop` wrapper field를 만들지 않고 `metadata` 또는 IOP native endpoint의 명시 필드로 전달한다. +외부 OpenAI API 호환 계층에서는 호환성을 위해 `model` 필드가 남을 수 있다. 그러나 내부 실행 개념에서는 모델 이름만으로 전체 실행을 설명하지 않고, `adapter`, `target`, `execution`, `node adapter`, `adapter execution` 같은 용어를 우선한다. IOP의 외부 실행 호출 계약은 OpenAI-compatible API 방식을 기본 표면으로 채택하되, IOP 고유의 workspace, session, agent, approval, artifact, notification 의미는 별도 `iop` wrapper field를 만들지 않고 `metadata` 또는 IOP native endpoint의 명시 필드로 전달한다. 외부 프로젝트가 참조할 OpenAI-compatible 요청 계약 원문은 [agent-contract/provided/openai-compatible-api.md](agent-contract/provided/openai-compatible-api.md)에 둔다. ## 아키텍처 @@ -264,7 +264,7 @@ Client의 장기 UI 기준은 Flutter 앱이며, 필요한 웹 표면은 Flutter - Client-Control Plane처럼 앱/브라우저 표면이 필요한 경계는 proto-socket WebSocket/WSS를 사용할 수 있다. Edge-Node 기본 transport를 WebSocket으로 전환하거나 gRPC, actor/FSM/plugin framework를 도입하는 것은 현재 단계의 기본 방향이 아니다. - OpenAI-compatible API 계층은 외부 모델 호출 호환을 위한 표면이며, 내부 실행 모델 전체를 대표하지 않는다. - OpenAI-compatible API는 현재 chat completions baseline을 가지며, Responses API 호환 표면까지 지원하는 방향으로 확장한다. -- IOP의 외부 통신 규약은 OpenAI-compatible API 방식을 기본 계약으로 채택하고, 나머지 IOP 전용 실행 문맥은 `metadata` 확장으로 전달한다. `iop` 같은 별도 wrapper field를 기본 표면에 추가하지 않는다. +- IOP의 외부 통신 규약은 OpenAI-compatible API 방식을 기본 계약으로 채택하고, 나머지 IOP 전용 실행 문맥은 `metadata` 확장으로 전달한다. `iop` 같은 별도 wrapper field를 기본 표면에 추가하지 않는다. 구체 요청 계약은 [agent-contract/provided/openai-compatible-api.md](agent-contract/provided/openai-compatible-api.md)를 기준으로 한다. - A2A API 계층은 agent 간 작업 위임과 상태 공유를 위한 표면이며, 단순 모델 호출 호환은 OpenAI-compatible API를 사용한다. NomadCode의 A2A 도입 시점은 아직 강제하지 않는다. - IOP native protocol은 OpenAI-compatible API나 A2A API를 대체하는 것이 아니라, Edge/Node 운영 제어와 CLI/session/command/event 같은 IOP 고유 기능을 제공하는 병행 표면이다. - Remote terminal bridge는 Edge/Node 운영 제어 기능으로 분류하며, OpenAI-compatible API가 아니라 IOP native protocol과 정책/audit 계층에서 다룬다. diff --git a/agent-contract/index.md b/agent-contract/index.md new file mode 100644 index 0000000..9d6ae54 --- /dev/null +++ b/agent-contract/index.md @@ -0,0 +1,20 @@ +# Agent Contract Index + +## 읽기 규칙 + +- 현재 작업이 외부 API, 런타임 호출, 프로젝트 간 연동, 요청/응답 스키마 계약에 닿을 때만 이 문서를 사용한다. +- 매칭되는 계약 문서만 읽는다. +- 계약 문서 경로가 없으면 계약을 추정하지 않고 사용자에게 확인한다. +- 계약 원문은 `agent-contract/provided/**` 또는 외부 프로젝트의 `agent-contract/provided/**` 한 곳에 둔다. +- `docs/`, `README`, `rules.md`에는 계약 원문을 복제하지 않고 계약 원문 경로만 둔다. + +## 제공 계약 + +| id | 읽는 조건 | path | +|----|-----------|------| +| `iop.openai-compatible-api` | OpenAI-compatible API, Responses API, Chat Completions, `model` route, Codex/CLI workspace, `metadata.workspace` | `agent-contract/provided/openai-compatible-api.md` | + +## 소비 계약 + +| id | 읽는 조건 | source | +|----|-----------|--------| diff --git a/agent-contract/provided/openai-compatible-api.md b/agent-contract/provided/openai-compatible-api.md new file mode 100644 index 0000000..e142afe --- /dev/null +++ b/agent-contract/provided/openai-compatible-api.md @@ -0,0 +1,79 @@ +# OpenAI-Compatible API Contract + +## 계약 메타 + +- id: `iop.openai-compatible-api` +- provider: `iop` +- status: active +- human docs: `docs/openai-compatible-api-contract.md` + +## 범위 + +이 문서는 외부 프로젝트가 IOP Edge의 OpenAI-compatible HTTP 표면을 호출할 때 확인할 계약 원문이다. +IOP 내부 실행은 `adapter + target` 기준이며, OpenAI-compatible 경계에서는 호환성을 위해 `model`을 사용한다. +IOP 고유 실행 문맥은 별도 `iop` wrapper field를 만들지 않고 OpenAI request의 `metadata`에 둔다. + +## Responses API + +Endpoint: + +```http +POST /v1/responses +Content-Type: application/json +``` + +CLI agent 실행으로 라우팅되는 요청의 최소 형태: + +```json +{ + "model": "codex", + "input": "현재 워크스페이스의 테스트 상태를 확인해줘.", + "metadata": { + "workspace": "/config/workspace/iop" + } +} +``` + +필드 의미: + +- `model`: Edge가 내부 `adapter + target`으로 해석할 외부 route 이름이다. +- `input`: agent에게 전달할 사용자 요청이다. +- `metadata.workspace`: CLI process를 실행할 작업 디렉터리다. CLI agent route에서는 필수 실행 문맥이다. + +금지: + +- `metadata.cli` 같은 CLI 전용 wrapper를 추가하지 않는다. +- root-level `iop` 같은 별도 wrapper field를 추가하지 않는다. +- workspace를 prompt 본문에 섞어 전달하지 않는다. + +현재 구현 메모: + +- `/v1/responses`는 non-streaming 요청만 지원한다. +- 기존 metadata 계약인 `metadata.request_id`, `metadata.nomadcode.task_id`, `metadata.nomadcode.source`, `metadata.inference.target`은 유지한다. +- CLI route의 `metadata.workspace`는 이 문서의 계약 기준이다. 구현은 이 값을 Edge service의 run workspace와 Node CLI adapter의 process working directory로 전달해야 한다. + +## Chat Completions + +`/v1/chat/completions`도 같은 metadata 원칙을 따른다. CLI route의 workspace는 `metadata.workspace`에 둔다. + +```json +{ + "model": "codex", + "messages": [ + { + "role": "user", + "content": "현재 워크스페이스의 테스트 상태를 확인해줘." + } + ], + "metadata": { + "workspace": "/config/workspace/iop" + } +} +``` + +## Routing + +Edge 설정이 `openai.model_routes[]`를 제공하면 `model`은 먼저 route catalog에서 해석된다. +매칭 route가 없으면 기존 fallback 규칙에 따라 `openai.target` 또는 요청의 `model`을 내부 target으로 사용한다. + +CLI agent를 OpenAI-compatible API로 노출할 때는 route catalog에서 해당 `model`을 명시적으로 `adapter: "cli"`와 target profile로 매핑하는 방식을 우선한다. diff --git a/apps/edge/README.md b/apps/edge/README.md index 1ab2674..37321a4 100644 --- a/apps/edge/README.md +++ b/apps/edge/README.md @@ -167,6 +167,8 @@ Edge 외부 입력은 OpenAI-compatible HTTP API와 A2A JSON-RPC HTTP API 두 이 표면은 외부 모델 클라이언트 호환을 위한 표준 경로다. Edge/Node 운영 제어, CLI logical session, background run, cancel/terminate-session, capabilities/status/session/transport command, node lifecycle event 같은 IOP 고유 기능은 OpenAI-compatible 요청에 억지로 싣지 않고 IOP native protocol(protobuf-socket) 계열에서 다룬다. +외부 프로젝트가 참조할 요청 계약 원문은 repo root의 `agent-contract/provided/openai-compatible-api.md`에 둔다. CLI agent route에서 사용할 workspace는 OpenAI request의 `metadata.workspace`에 둔다. + ```yaml openai: enabled: true diff --git a/apps/node/internal/adapters/cli/opencode_sse_blackbox_test.go b/apps/node/internal/adapters/cli/opencode_sse_blackbox_test.go index 7e1a6ce..87d8b65 100644 --- a/apps/node/internal/adapters/cli/opencode_sse_blackbox_test.go +++ b/apps/node/internal/adapters/cli/opencode_sse_blackbox_test.go @@ -41,6 +41,10 @@ func newOpencodeFakeServer(t *testing.T, sessionID string) (*opencodeFakeServer, } mux := http.NewServeMux() eventHandler := func(w http.ResponseWriter, r *http.Request) { + s.mu.Lock() + events := s.events + s.mu.Unlock() + w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.WriteHeader(http.StatusOK) @@ -52,7 +56,7 @@ func newOpencodeFakeServer(t *testing.T, sessionID string) (*opencodeFakeServer, select { case <-r.Context().Done(): return - case ev, ok := <-s.events: + case ev, ok := <-events: if !ok { return } diff --git a/docs/openai-compatible-api-contract.md b/docs/openai-compatible-api-contract.md new file mode 100644 index 0000000..1abca9a --- /dev/null +++ b/docs/openai-compatible-api-contract.md @@ -0,0 +1,7 @@ +# OpenAI-Compatible API Contract + +계약 원문은 [agent-contract/provided/openai-compatible-api.md](../agent-contract/provided/openai-compatible-api.md)다. + +이 문서는 사람용 안내와 기존 링크 유지를 위한 포인터다. 요청 스키마, 필드 의미, 금지 사항, 구현 메모는 계약 원문을 기준으로 한다. + +에이전트 작업에서는 [agent-contract/index.md](../agent-contract/index.md)의 라우팅 규칙을 먼저 따른다.