12 KiB
12 KiB
Edge-Node Runtime Wire Contract
계약 메타
- id:
iop.edge-node-runtime-wire - boundary:
inner - status: active
- 원본 경로:
proto/iop/runtime.protoapps/edge/internal/transport/server.goapps/node/internal/transport/client.goapps/node/internal/transport/session.goapps/node/internal/transport/parser.goapps/node/internal/bootstrap/runtime_supervisor.goapps/edge/internal/transport/connection_handlers.goapps/edge/internal/service/model_queue_release.goapps/edge/internal/service/status_provider.goapps/edge/internal/node/mapper.goapps/node/internal/adapters/config_set.go
- human docs:
apps/edge/README.mdapps/node/README.md
읽는 조건
- Edge-Node TCP/protobuf transport, initial/reconnect supervision, register/dispatch-ready handshake, connection generation fencing, run stream, provider raw tunnel, cancel, node command, node config refresh를 바꿀 때
NodeReadyRequest,NodeReadyResponse,RunRequest,RunEvent,ProviderTunnelRequest,ProviderTunnelFrame,CancelRequest,NodeCommandRequest,NodeCommandResponse,NodeConfigPayload,NodeConfigRefresh*필드를 바꿀 때- node adapter 설정 payload나 runtime config가 Edge에서 Node로 전달되는 방식을 바꿀 때
범위
이 계약은 Edge와 Node 사이의 내부 TCP proto-socket 경계다. Edge는 Node 연결을 수락하고, Node는 연결 직후 등록 요청을 보낸다. 실행 요청과 이벤트 스트림, 취소, 조회성 명령, 설정 refresh는 같은 내부 wire 계열에서 처리한다.
주요 흐름
- register와 readiness: Node가
RegisterRequest를 보내고 Edge가RegisterResponse로 수락 여부와NodeConfigPayload를 돌려준다. accepted registration은 Node ID의 현재 ownership을 pending으로 claim할 뿐 dispatch 가능 상태가 아니다. Node는 config 적용, adapter start, session handler 설치 뒤NodeReadyRequest(node_id)를 보내고, Edge가 current owner를 dispatch-ready로 전환한 뒤NodeReadyResponse로 ack한다. 이 ready ack 전에는 run, provider tunnel, command, config-refresh push와 connected availability/event가 열리지 않는다. - connectivity supervision: Node daemon은 Fx startup 전에 원격 연결 성공을 요구하지 않고 단일 supervisor goroutine이 initial dial과 established-session reconnect를 같은 policy로 직렬 처리한다. retryable 원격 실패는 재시도하고 local config/credential fatal error, 유한 retry exhaustion, local shutdown만 process terminal로 구분한다.
- disconnect/reconnect: current dispatch-ready owner의 close/heartbeat timeout만 해당 connection generation을 fence한다. Edge는 같은 authoritative lifecycle에서 provider lease를 정확히 한 번 반환하고 resource를 offline/excluded로 만든 뒤 queue를 live candidate 기준으로 재평가한다. accepted Node의 ready transition은 새 generation resource를 활성화하고 기존 waiter를 즉시 pump한다. stale/rejected connection callback은 live state나 lifecycle event를 바꾸지 않는다.
- execution: Edge가
RunRequest를 보내고 Node가RunEventstream으로 실행 상태를 보낸다. - provider raw tunnel: Edge가 기존 Edge-Node socket으로
ProviderTunnelRequest를 보내고 Node가 provider HTTP/SSE 요청을 연 뒤ProviderTunnelFramestream으로 provider status/header/body/end/error/usage 후보를 sequence와 함께 돌려준다. 이 경로는 OpenAI-compatible provider passthrough용이며RunEvent실행 stream과 분리된다. - provider-pool mixed dispatch: Edge service는 model group provider candidate를 선택한 뒤, 같은 selected provider/queue lease로 OpenAI-compatible provider에는
ProviderTunnelRequest, Ollama/CLI/native provider에는 normalizedRunRequest를 보낸다. Edge-Node wire는 client-provided response path selector를 받지 않고, provider type만으로 후보를 제외하지 않는다. - cancel: Edge가
CancelRequest를 보내며CANCEL_RUN과TERMINATE_SESSION을 구분한다. - command: Edge가
NodeCommandRequest를 보내고 Node가NodeCommandResponse로 usage/capabilities/session/transport/provider 상태를 응답한다. - refresh: Edge가
NodeConfigRefreshRequest로 새 config payload를 보내고 Node가NodeConfigRefreshResponse로 적용/재시작 필요/실패를 응답한다.
필드 의미
RunRequest.adapter,RunRequest.target: 내부 실행 식별자다. 외부 OpenAI-compatiblemodel은 Edge 입력 표면에서 이 둘로 변환되어야 한다.RunRequest.workspace: CLI agent route 같은 workspace-bound 실행의 작업 디렉터리다.RunRequest.input: adapter가 해석할 실행 입력이다. CLI 실행에서는 prompt 계열 입력으로 변환된다.RunRequest.metadata: caller-defined 실행 metadata다. workspace 자체는 별도workspace필드로 전달한다.RunEvent.type:start,delta,complete,error,cancelled같은 실행 이벤트 종류다.ProviderTunnelRequestis the protobuf request for opening a provider HTTP request over the existing Edge-Node socket. It carriesadapter,target,method,path,headers, final serializedbody,stream,timeout_sec,metadata,session_id, andoperation, separately from normalizedRunRequestexecution.ProviderTunnelRequest.operationis protobuf field 13. It identifies a named profile operation (models,chat_completions,messages,count_tokens, orresponses); when it is empty,pathremains the mixed-version fallback.SubmitProviderTunnelRequest.BuildBodyis Edge-local only. After provider-pool selection determines the served target, Edge invokes it and serializes its returned bytes into protobufProviderTunnelRequest.body. It is not a protobuf field.- The resolved
ConcreteProtocolProfiletravels in nestedOpenAICompatAdapterConfig.protocol_profileinside the Node configuration payload. Tunnel requests carry the selected operation and bytes, not profile configuration. ProviderTunnelFrameis the ordered response frame.bodyis the passthrough source of truth and is not sent throughRunEvent.deltaor the Edge event bus;usageandmetadataare observation candidates and are never merged into the body.RESPONSE_STARToccurs at most once,BODYoccurs zero or more times, and exactly one terminalENDorERRORoccurs.USAGEis observation-only.- tunnel cancellation: HTTP caller disconnect, response wait timeout, 또는 Edge write failure가 발생하면 Edge는 같은 run id에 대한
CancelRequest(CANCEL_RUN)을 보내 upstream provider request 중단을 요청한다. Node adapter는 provider request context cancellation을 관측하고 ordered error/end semantics를 유지해야 한다. RunEvent.metadata["openai_tool_calls"]: OpenAI-compatible provider adapter가 nativetool_calls를 반환했을 때 완료 이벤트에 싣는 JSON 배열이다. Edge OpenAI-compatible 표면은 이 값을message.tool_calls또는 streamdelta.tool_calls로 복원한다. provider assistant content 텍스트를 이 값으로 파싱/합성하지 않는다.RunEvent.metadata["openai_text_tool_fallback"]: OpenAI-compatible provider adapter가 backend native tool API 거부 후tools/tool_choice를 제거하고 text tool-call instruction으로 재시도했을 때"true"를 싣는다. 이 instruction은 backend가 system role 위치를 거부하지 않도록 leading system message에 병합한다. Edge는 이 표시가 있는 실행에서만 assistant content의 text tool-call을 OpenAI-compatibletool_calls로 복원할 수 있다.NodeCommandRequest.type: 실행이 아닌 조회/제어성 명령이다. adapter execution 요청과 섞지 않는다.NodeConfigPayload.adapters: Edge가 Node에 내려주는 adapter instance 설정이다.NodeReadyRequest.node_id:RegisterResponse가 돌려준 Node identity다. Edge registry의 internal connection generation은 이 wire/config field로 노출하지 않으며, Edge는(node_id, current client)ownership 비교로 stale ready를 거부한다.NodeReadyResponse.ready: current pending owner의 첫 ready transition과 이미 ready인 같은 owner의 duplicate ready에서 true다. 첫 transition만 provider resource activation, stranded provider-pool waiter pump,node.connectedevent를 만든다. stale/superseded/rejected connection은 false와 reason을 받고 session을 닫아 reconnect해야 한다.AdapterConfig.name: node 내부 stable adapter instance identity다. 비어 있으면 legacy single-instance type 이름과 동등하다.NodeRuntimeConfig.concurrency: legacy compatibility runtime metadata다. 실행 admission은 이 값을 node-wide global gate로 사용하지 않고 provider/resource capacity를 기준으로 한다. Node store 위치나 CLI 실행 작업 디렉터리는 이 runtime payload에 싣지 않는다.reconnect.interval_sec,reconnect.max_attempts: initial connect와 established-session reconnect에 공통 적용된다. 명시적max_attempts=0은 local shutdown까지 unlimited, 생략은 기본값10, 양수는 정확한 유한 attempt limit, 음수는 validation error다. unlimited mode의interval_sec는 양수여야 하며 생략은 기본값10을 사용한다. 유한 exhaustion과 non-retryable 오류는 exit code 1, local shutdown은 정상 종료다.ProviderSnapshot: legacy wire name을 유지하지만 Node 아래 resource/provider 상태 snapshot으로 해석한다.category가api,cli,local_inferenceresource kind를 나타내며, provider-pool dispatch 대상은 Edge configmodels[].providers가 참조한 resource뿐이다.in_flight와long_in_flight는node_id + provider_idlease state의 현재 점유다.queued는 Edge queue에서 해당 provider를 live candidate로 포함하는 고유 pending request 수이고long_queued는 그중 long request 수이므로 여러 provider snapshot에 같은 request가 candidate pressure로 나타날 수 있다.- configured Node가 disconnected/pending이면 Node snapshot은
connected=false를 유지하고 provider catalog entry도 남는다. enabled provider의 effective snapshot은status=unavailable,health=offline, capacity/in-flight/queued/long-context 관련 수치가 모두 0이다. reconnect ready 뒤에는 같은 resource identity의 새 generation으로 configured capacity와 admission eligibility가 복구된다. - Node adapter instance는 normalized
RunRequest와ProviderTunnelRequest가 공유하는 local capacity gate를 사용한다. 이 gate는 Edge provider lease를 복제하는 분산 admission이 아니라 Edge queue를 우회한 실행으로부터 같은 backend를 보호하는 defense-in-depth다.
금지 사항
- Control Plane이나 Client가 Node에 직접 연결하거나 직접 스케줄링하는 계약을 만들지 않는다.
- 외부 API의
model용어를 이 내부 경계의 대표 실행 식별자로 되살리지 않는다. proto/gen/iop/*.pb.go생성물을 직접 수정하지 않는다.- transport handler에서 console/HTTP/A2A 표면 응답을 직접 만들지 않는다. 표면별 변환은 Edge service/input 계층에 둔다.
- Node address, token, transport internals를 Control Plane status 계약으로 노출하지 않는다.
- accepted registration만으로 Node를 dispatch candidate, connected snapshot/event 또는 config refresh recipient로 취급하지 않는다.
- provider lease 반환, generation fencing, queue settlement 같은 correctness 전이를 drop 가능한 node event fanout의 성공에 의존시키지 않는다.
- OS service/Task Scheduler restart를 retryable initial connect 또는 장기 outage 복구의 correctness owner로 사용하지 않는다.
변경 시 확인할 코드/테스트
proto/iop/runtime.protoapps/edge/internal/transport/*_test.goapps/node/internal/transport/*_test.goapps/edge/internal/bootstrap/reconnect_readiness_integration_test.goapps/edge/internal/openai/provider_dispatch_test.goapps/edge/internal/openai/provider_selection_test.goapps/edge/internal/openai/provider_tunnel_test.goapps/edge/internal/openai/cancellation_routes_test.goapps/node/internal/adapters/openai_compat/*_test.goapps/node/internal/adapters/vllm/*_test.goapps/edge/internal/node/mapper_test.goapps/node/internal/adapters/config_set_test.goapps/node/internal/adapters/adapters_blackbox_test.go- proto 변경 시
make proto, Client가 소비하면make proto-dart