iop/agent-spec/runtime/edge-node-execution.md

186 lines
9.6 KiB
Markdown

---
spec_doc_type: spec
spec_id: runtime/edge-node-execution
status: 부분
source_evidence:
- type: contract
path: agent-contract/inner/edge-node-runtime-wire.md
notes: Edge-Node register, run stream, cancel, node command, config refresh wire 계약
- type: code
path: proto/iop/runtime.proto
notes: RunRequest, RunEvent, CancelRequest, NodeCommandRequest, RegisterRequest, NodeConfigPayload 원문
- type: code
path: apps/edge/internal/transport/server.go
notes: Edge TCP proto-socket server, node register handshake, event relay
- type: code
path: apps/edge/internal/service/run_dispatch.go
notes: surface-neutral SubmitRun, provider tunnel dispatch, cancel request 생성
- type: code
path: apps/node/internal/node/node.go
notes: Node transport handler, adapter 실행, cancel, command, config refresh 처리
- type: code
path: apps/node/internal/adapters/openai_compat/openai_compat.go
notes: OpenAI-compatible provider usage payload를 RuntimeEvent usage로 변환
- type: code
path: apps/node/internal/adapters/vllm/vllm.go
notes: vLLM usage payload의 reasoning/cached token 변환
- type: test
path: apps/edge/internal/transport/server_test.go
notes: Edge transport server 단위 검증
- type: test
path: apps/node/internal/node/node_test.go
notes: Node 실행 처리 단위 검증
- type: test
path: apps/node/internal/adapters/openai_compat/openai_compat_test.go
notes: OpenAI-compatible provider usage breakdown 검증
- type: test
path: apps/node/internal/adapters/vllm/vllm_test.go
notes: vLLM usage breakdown 검증
---
# 스펙: Edge-Node 실행 경로
## 목적
Edge와 Node 사이에 현재 구현된 실행 기능을 기능 단위로 정리한다. 코드 배치 규칙이나 도메인별 작업 지침은 domain rule을 따른다.
## 기능 목록
| 기능 | 설명 |
|------|------|
| Node token 등록 | Node가 Edge TCP proto-socket endpoint에 연결한 뒤 `RegisterRequest.token`으로 등록한다. |
| Node config payload 전달 | Edge가 token에 매칭되는 node record를 찾아 `NodeConfigPayload``RegisterResponse`에 담아 내려준다. |
| 등록 실패 처리 | unknown token, duplicate connection, config payload build failure를 register response와 node lifecycle event로 표현한다. |
| 실행 요청 전달 | Edge service가 `SubmitRun` 요청을 `RunRequest`로 만들어 선택된 Node에 보낸다. 명시 node가 없고 연결 node가 1개면 single-node fallback을 사용한다. |
| adapter 실행 | Node가 `RunRequest.adapter`로 adapter instance를 찾고 adapter `Execute`를 호출한다. admission은 adapter `Capabilities().MaxConcurrency` 기준이다. |
| 실행 이벤트 스트림 | Node adapter가 낸 start, delta, reasoning_delta, complete, error, cancelled 이벤트를 `RunEvent`로 Edge에 relay한다. |
| provider raw tunnel | Edge가 `ProviderTunnelRequest`를 보내면 Node가 provider HTTP/SSE response를 열고 ordered `ProviderTunnelFrame`으로 status/header/body/end/error/usage 후보를 relay한다. |
| mixed provider dispatch wire | provider-pool model group은 Edge service에서 provider를 먼저 선택한 뒤 OpenAI-compatible provider에는 `ProviderTunnelRequest`, Ollama/CLI/native provider에는 normalized `RunRequest`를 보낸다. |
| usage breakdown relay | `RunEvent.usage``ProviderTunnelFrame.usage`는 provider가 보고한 input/output/reasoning/cached input token count를 Edge 관측 계층으로 전달한다. |
| terminal event 합성 | adapter가 terminal event 없이 종료하면 Node가 terminal event를 합성한다. |
| run cancel | Edge가 `CancelRequest`를 보내면 Node run manager가 active run context를 cancel한다. |
| logical session 종료 | adapter가 `SessionTerminator`를 구현한 경우 `TERMINATE_SESSION`으로 session을 종료한다. 모든 adapter 공통 기능은 아니다. |
| Node command | capabilities, transport status, usage status, session list, ollama API 계열 조회/제어성 command를 실행 요청과 분리해 처리한다. |
| Node local run store | Node가 run id, adapter, target, session id, background, status, timestamps, error를 SQLite에 기록한다. |
| Edge event fanout | Edge event bus가 run event와 node lifecycle event를 in-process subscriber에게 fanout한다. |
## 범위
- 포함: Edge-Node TCP/protobuf transport, register handshake, run/cancel/command, provider raw tunnel, Node adapter execution, Edge event bus fanout, Node local run store.
- 제외: OpenAI-compatible/A2A HTTP request shape, Control Plane 운영 wire, provider-pool config refresh 상세, durable global history/audit.
## 주요 흐름
### Node 등록
```mermaid
sequenceDiagram
participant Node
participant EdgeTransport as Edge transport
participant NodeStore as Edge NodeStore
Node->>EdgeTransport: TCP connect
Node->>EdgeTransport: RegisterRequest(token)
EdgeTransport->>NodeStore: token으로 NodeRecord 조회
alt token valid
EdgeTransport->>EdgeTransport: NodeConfigPayload 생성
EdgeTransport-->>Node: RegisterResponse(accepted=true, config)
EdgeTransport->>EdgeTransport: live registry 등록
else token invalid or duplicate
EdgeTransport-->>Node: RegisterResponse(accepted=false, reason)
end
```
### 실행 요청과 이벤트
```mermaid
sequenceDiagram
participant Caller
participant EdgeService as Edge service
participant EdgeTransport as Edge transport
participant Node
participant Adapter
Caller->>EdgeService: SubmitRun(adapter, target, input)
EdgeService->>EdgeService: Node 선택
EdgeService->>EdgeTransport: RunRequest
EdgeTransport->>Node: RunRequest
Node->>Node: adapter instance resolve
Node->>Adapter: Execute(spec)
Adapter-->>Node: RuntimeEvent(delta/start/complete)
Node-->>EdgeTransport: RunEvent
EdgeTransport-->>Caller: run stream
```
### Provider raw tunnel
```mermaid
sequenceDiagram
participant OpenAI as Edge OpenAI surface
participant EdgeService as Edge service
participant Node
participant Provider
OpenAI->>EdgeService: SubmitProviderTunnel
EdgeService->>Node: ProviderTunnelRequest
Node->>Provider: HTTP/SSE request
Provider-->>Node: status/header/body
Node-->>EdgeService: ProviderTunnelFrame sequence
EdgeService-->>OpenAI: request-bound frame stream
```
### 취소와 session 종료
```mermaid
sequenceDiagram
participant EdgeService as Edge service
participant Node
participant Adapter
alt cancel run
EdgeService->>Node: CancelRequest(CANCEL_RUN, run_id)
Node->>Node: active run context cancel
else terminate session
EdgeService->>Node: CancelRequest(TERMINATE_SESSION, adapter, target, session_id)
Node->>Adapter: TerminateSession(target, session_id)
end
```
## 계약
- `iop.edge-node-runtime-wire`: `agent-contract/inner/edge-node-runtime-wire.md`
- proto 원문: `proto/iop/runtime.proto`
## 설정/데이터/이벤트
- Edge의 node source of truth는 `configs/edge.yaml``packages/go/config``nodes[]` 구조다.
- `RunEvent`는 adapter execution stream이고, `EdgeNodeEvent`는 node lifecycle/control event다.
- `ProviderTunnelFrame.body`는 OpenAI-compatible provider passthrough의 source of truth이며 `RunEvent.delta`나 Edge event bus payload로 보내지 않는다.
- `ProviderTunnelFrame.usage``metadata`는 sideband observation 후보이며 pure passthrough body에 합쳐지지 않는다.
- provider-pool mixed dispatch에서 `ProviderTunnelRequest``RunRequest` 중 어느 wire를 사용할지는 selected provider capability에서 파생되며, client request metadata selector로 결정하지 않는다.
- `Usage.reasoning_tokens``Usage.cached_input_tokens`는 provider가 별도 보고한 경우에만 채워지는 optional breakdown이다.
- Node local DB는 기본 `file:iop.db?cache=shared&mode=rwc`로 열린다.
- heartbeat는 Edge와 Node transport 양쪽에서 30초 interval, 45초 wait 기준을 사용한다.
## 검증
- `go test ./apps/edge/internal/transport ./apps/edge/internal/service ./apps/edge/internal/node`
- `go test ./apps/node/internal/transport ./apps/node/internal/node ./apps/node/internal/router ./apps/node/internal/adapters ./apps/node/internal/store`
- `go test ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm`
- `make test-e2e` - Edge-Node와 OpenAI 보조 smoke를 함께 실행한다. runtime path 변경 시 사용자 흐름 검증을 대체하지 않는다.
## 한계와 주의사항
- mTLS helper는 존재하지만 현재 Edge-Node transport 설정에는 연결되어 있지 않다.
- `TERMINATE_SESSION`은 모든 adapter에 공통으로 보장되는 기능이 아니다.
- Node store는 전역 query/audit API가 아니다. 상위 운영 이력은 별도 설계가 필요하다.
- provider raw tunnel은 기존 socket 위 request-bound stream이다. 별도 Node stream server나 Edge의 provider direct access 경로가 아니다.
- usage breakdown은 provider-reported 값만 전달한다. provider가 보고하지 않은 reasoning token을 Node나 Edge가 추정하지 않는다.
## 변경 기록
- 2026-07-07: 현재 코드, 계약, README 기준으로 bootstrap spec 작성.
- 2026-07-07: 기능 목록 중심으로 축소하고 주요 흐름을 Mermaid sequence diagram으로 정리.
- 2026-07-08: Provider raw tunnel 실행 흐름과 passthrough event/data 경계를 현재 계약 기준으로 반영.
- 2026-07-10: `RunEvent.usage`/`ProviderTunnelFrame.usage`의 input/output/reasoning/cached input breakdown 전달 기준을 반영.
- 2026-07-12: Model Group Mixed Provider Dispatch 종료 검토 기준으로 selected provider capability에서 파생되는 `ProviderTunnelRequest`/`RunRequest` 분기 경계를 반영.