누적된 잠금·승인·증거 체인이 구현과 완료를 반복 차단해 작업 비용을 키웠다. 보안·데이터 손상·명시적 외부 의존성만 차단 조건으로 남기고 로드맵과 스킬의 기본 흐름을 단순화한다.
112 lines
12 KiB
Markdown
112 lines
12 KiB
Markdown
# SDD: [surface-01] Inference API Surface와 실행 Lifecycle 책임 경계 리팩터링
|
|
|
|
## 위치
|
|
|
|
- Milestone: [Milestone 문서](../../../phase/knowledge-tool-optimization-extension/milestones/inference-api-surface-execution-lifecycle-refactor.md)
|
|
- Phase: [PHASE.md](../../../phase/knowledge-tool-optimization-extension/PHASE.md)
|
|
|
|
## 상태
|
|
|
|
[확정]
|
|
|
|
## 결정 사항
|
|
|
|
- 결정 기록:
|
|
- [x] [D01] OpenAI, Anthropic, Gemini wire contract는 공통 DTO로 합치지 않고 각 surface adapter에 남긴다.
|
|
- [x] [D02] provider admission·dispatch·attempt·cancel은 기존 `runService`/`SubmitProviderPool` 경계를 재사용하고, protocol별 실행 준비와 endpoint commit·terminal은 각 기존 runtime이 소유한다. 하나의 universal lifecycle owner나 terminal DTO를 만들지 않는다.
|
|
- [x] [D03] 리팩터링은 caller-visible behavior, provider selection, native option passthrough와 benchmark 조건을 바꾸지 않는다.
|
|
- [x] [D04] `iop-s0` `[bench-02]` 결과가 있으면 리팩터링의 비교 baseline으로 사용하되, 결과 상태나 workspace lock을 구현 선행 조건으로 사용하지 않는다.
|
|
- [x] [D05] benchmark 비교가 현재 작업 범위에 포함되면 제품 code/spec/contract와 benchmark-only harness/data의 차이를 분리한다.
|
|
- [x] [D06] 완료 evidence는 deterministic characterization과 기존 benchmark artifact 비교를 기본으로 한다. 외부 provider live smoke나 benchmark 재실행은 별도 환경·비용 승인이 있을 때만 추가한다.
|
|
|
|
## 문제 / 비목표
|
|
|
|
- 문제: 현재 Edge는 `apps/edge/internal/service`의 `SubmitProviderPool`/`SubmitProviderTunnel`로 provider admission과 dispatch를 이미 공통화했지만, ingress orchestration과 endpoint lifecycle 책임은 `apps/edge/internal/openai` 안에서 경로별로 다르게 조립된다. 특히 Gemini ingress는 Chat request와 내부 HTTP 요청/response writer를 합성해 `handleChatCompletions`에 재진입하므로 Chat handler의 validation·오류·commit 방식에 간접 결합된다. Anthropic native/bridge, OpenAI passthrough/normalized, single-request는 서로 다른 terminal contract를 가지므로 기존 service 경계를 넘어 request/terminal schema까지 공통화하면 회귀 위험이 크다.
|
|
- 비목표:
|
|
- 서로 다른 외부 API field와 event를 하나의 범용 request/response schema로 축소
|
|
- 신규 기능, provider 최적화, routing/retry/filter 정책 변경
|
|
- Control Plane, Edge-Node wire, config/proto 계약 변경
|
|
- benchmark pipeline 또는 비교 결과 수정
|
|
- 기존 `SubmitProviderPool`과 겹치는 새 all-surface executor 또는 모든 endpoint terminal을 소유하는 중앙 state machine 도입
|
|
|
|
## Source of Truth
|
|
|
|
| 영역 | 기준 | 메모 |
|
|
|------|------|------|
|
|
| Roadmap | [Milestone 문서](../../../phase/knowledge-tool-optimization-extension/milestones/inference-api-surface-execution-lifecycle-refactor.md) | 범위와 Task 원장 |
|
|
| API Contracts | [OpenAI-Compatible API](../../../../agent-contract/outer/openai-compatible-api.md), [Anthropic-Compatible Messages API](../../../../agent-contract/outer/anthropic-compatible-api.md), [Gemini-Compatible API](../../../../agent-contract/outer/gemini-compatible-api.md) | caller-visible 동작 baseline |
|
|
| Current Spec | [OpenAI-Compatible 입력 표면](../../../../agent-spec/input/openai-compatible-surface.md), [Stream Evidence Gate](../../../../agent-spec/runtime/stream-evidence-gate.md) | 현재 구현 책임과 lifecycle 설명 |
|
|
| Surface/Ingress Code | `apps/edge/internal/openai/` | surface codec, validation, projector, Stream Evidence Gate와 operation-specific ingress orchestration 구현 |
|
|
| Provider Execution | `apps/edge/internal/service/`의 `SubmitProviderPool`, `SubmitProviderTunnel`, `SubmitRun` | provider admission·dispatch·attempt·Node cancellation의 기존 source of truth |
|
|
| Benchmark Baseline | [iop-s0 `[bench-02]` Milestone](../../../../../iop-s0/agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-one-shot-agent-model-comparison.md) | 구현 시작을 잠그는 비교 work owner와 최종 evidence |
|
|
| User Decision | 없음 | D01-D06은 기존 code/contract와 사용자 요청 범위에서 도출한 기술 기준이며 신규 제품 결정은 없다. |
|
|
|
|
## State Machine
|
|
|
|
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|
|
|------|-----------|-----------|------|
|
|
| `surface_received` | endpoint가 request와 surface auth header를 수신 | `surface_validated`, `terminal_error`, `cancelled` | surface adapter가 credential form과 body를 소유 |
|
|
| `surface_validated` | surface validation, 공통 principal resolver와 route snapshot 완료 | `admitted`, `terminal_error`, `cancelled` | surface adapter + shared auth/projection resolver |
|
|
| `admitted` | immutable protocol-specific execution input이 준비됨 | `attempting`, `terminal_error`, `cancelled` | ingress orchestration이 기존 service request를 구성 |
|
|
| `attempting` | 기존 service가 provider candidate와 operation을 확정하고 attempt 시작 | `pre_commit`, `terminal_error`, `cancelled` | `SubmitProviderPool`/`SubmitProviderTunnel` |
|
|
| `pre_commit` | endpoint runtime이 아직 caller-visible byte를 commit하지 않음 | `committed`, `terminal_error`, `cancelled` | Stream Evidence Gate, endpoint projector 또는 single-request coordinator |
|
|
| `committed` | endpoint-native status/header/opening event가 최초 commit됨 | `terminal_success`, `terminal_error`, `cancelled` | surface별 wire writer/terminal state machine |
|
|
| `terminal_success` | endpoint-native success terminal과 해당 경로의 usage finalization 완료 | 종료 | endpoint runtime의 exactly-once terminal evidence |
|
|
| `terminal_error` | pre-commit envelope 또는 post-commit endpoint-native error terminal 확정 | 종료 | surface error projection과 기존 terminal winner |
|
|
| `cancelled` | caller context 취소가 해당 경로의 terminal winner가 됨 | 종료 | endpoint가 service/Node cancel을 전파하고 이후 wire write 금지 |
|
|
|
|
State invariant:
|
|
|
|
- 한 public request의 각 lifecycle 전이에는 owner가 하나만 있고 endpoint terminal winner도 하나만 존재한다. 모든 전이를 하나의 새 중앙 owner로 이동하지 않는다.
|
|
- surface adapter는 credential form, decode/validation/error/SSE 형식을 소유한다. 공통 auth/projection resolver와 service provider selection/attempt identity/Node cancellation은 중복 구현하지 않는다.
|
|
- operation-specific ingress 실행 함수는 OpenAI/Anthropic/Gemini public DTO나 `http.ResponseWriter`를 `apps/edge/internal/service`로 넘기지 않는다. 기존 service request/result를 사용하고 endpoint projector hook도 service에 주입하지 않는다.
|
|
- Stream Evidence Gate와 single-request coordinator는 기존 pre-commit/terminal state machine을 계속 소유하며 새 universal terminal DTO로 치환하지 않는다.
|
|
- raw passthrough body는 route에 필요한 bounded field만 읽고 unknown/provider-native field를 제거하지 않는다. translated body도 `extra_body`를 포함한 생성 결과를 손실 없이 전달한다.
|
|
- commit 전후의 오류 projection은 surface 계약을 따르며, 다른 surface의 handler나 HTTP round-trip을 통해 얻지 않는다.
|
|
|
|
## Interface Contract
|
|
|
|
- 계약 원문: [OpenAI-Compatible API](../../../../agent-contract/outer/openai-compatible-api.md), [Anthropic-Compatible Messages API](../../../../agent-contract/outer/anthropic-compatible-api.md), [Gemini-Compatible API](../../../../agent-contract/outer/gemini-compatible-api.md)
|
|
- 입력:
|
|
- authenticated request snapshot: principal, public route/model, immutable projection/binding revision과 cancellation context다.
|
|
- protocol operation: Chat Completions, Responses, Anthropic Messages 또는 surface가 명시적으로 변환한 upstream operation이다.
|
|
- response mode: buffered, normalized stream 또는 raw tunnel stream의 실행 의미다.
|
|
- execution payload: raw passthrough body 또는 surface가 검증해 만든 provider request body/normalized input이다.
|
|
- 출력:
|
|
- 기존 service result: selected execution path, caller에 노출하지 않는 attempt attribution과 run/tunnel handle이다.
|
|
- endpoint runtime input: 기존 RunEvent/tunnel frame/single-request progress 중 해당 operation이 이미 사용하는 bounded 결과다.
|
|
- endpoint terminal: surface projector나 기존 coordinator가 자기 error/terminal contract로 확정하며 공통 service 결과 타입으로 만들지 않는다.
|
|
- 금지:
|
|
- Gemini가 내부 `http.Request`/`ResponseWriter`를 만들어 Chat handler를 호출한다.
|
|
- 공통 service가 Gemini/Anthropic/OpenAI public response envelope나 SSE event 이름을 생성한다.
|
|
- endpoint projector callback이나 `http.ResponseWriter`를 provider service interface에 추가한다.
|
|
- 기존 `SubmitProviderPool`과 같은 capability를 포장만 바꿔 중복 구현한다.
|
|
- 공통 DTO를 만들기 위해 surface-specific field, unknown field 또는 provider-native option을 버린다.
|
|
- 리팩터링 중 provider fallback, retry budget, output filter, single-request stage semantics를 변경한다.
|
|
|
|
## Acceptance Scenarios
|
|
|
|
| ID | Milestone Task | Given | When | Then |
|
|
|----|----------------|-------|------|------|
|
|
| S01 | `baseline-freeze` | 현재 동작을 비교할 기준이 필요함 | 기존 characterization 또는 사용 가능한 benchmark 결과를 선택 | target/current revision과 제품 delta, benchmark-only delta가 구분된다. benchmark 상태와 workspace lock은 선행 조건이 아니다. |
|
|
| S02 | `boundary-map` | 세 외부 API와 raw/normalized/single-request 경로가 존재 | 책임/의존 지도를 검토 | surface, shared auth resolver, 기존 service, endpoint runtime의 단계별 소유권이 겹치지 않고 API/terminal DTO 공통화가 비범위로 표시된다. |
|
|
| S03 | `execution-entrypoint` | Gemini direct와 virtual preset 요청 | Gemini ingress를 실행 | Chat handler 내부 HTTP 재진입이나 중복 service port 없이 기존 provider/preset execution capability를 호출하고 Gemini contract 결과를 반환한다. |
|
|
| S04 | `surface-adapters` | 각 surface의 정상·validation·auth·provider 오류 입력 | endpoint별 adapter/projector를 실행 | 외부 status, error envelope, model echo와 SSE event 순서가 baseline과 동일하다. |
|
|
| S05 | `lifecycle-owner` | 정상, provider 오류, timeout, pre/post-commit failure, caller cancel | stream/non-stream lifecycle을 종료 | 단계별 owner가 하나이고 public terminal과 request usage가 exactly once이며 취소 뒤 추가 wire write가 없다. |
|
|
| S06 | `native-option-preservation` | unknown OpenAI field, Anthropic native extension, Gemini thinking `extra_body`와 model alias | raw 또는 translated dispatch를 수행 | selected provider body에 허용 field가 보존되고 unsupported 의미는 기존 provider/surface 오류로 귀결된다. |
|
|
| S07 | `contract-regression` | OpenAI Chat/Responses, Anthropic Messages, Gemini direct/single-request characterization matrix와 기존 benchmark artifact | deterministic package test와 artifact 비교를 실행 | caller-visible behavior와 benchmark admission 조건에 의도하지 않은 차이가 없고 별도 승인 없는 live provider 호출은 발생하지 않는다. |
|
|
| S08 | `spec-sync` | 리팩터링 구현과 회귀 검증 완료 | contract source pointer와 implementation spec을 점검 | 외부 계약 의미는 유지되고 source path·책임 경계·현재 스펙이 실제 코드와 일치하며 기존 계약과 다른 제품 동작은 별도 후보로 분리된다. |
|
|
|
|
## Cross-repo Reference
|
|
|
|
- 참고: [iop-s0 `[bench-02]` IOP 원샷 Agent 모델 비교 벤치마크](../../../../../iop-s0/agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-one-shot-agent-model-comparison.md)
|
|
- 사용 가능한 결과가 있으면 비교 자료로 사용한다. 해당 Milestone의 상태나 workspace lock은 이 구현의 선행 조건이 아니다.
|
|
|
|
## 사용자 리뷰 이력
|
|
|
|
- 2026-08-13: API 방식 자체가 달라 범용 request/terminal interface가 어렵다는 사용자 문제 제기를 바탕으로, benchmark 완료 뒤 구현하되 현시점에는 surface 계약 분리와 기존 provider service 경계 재사용을 기준으로 Milestone을 먼저 작성했다.
|
|
|
|
## 작업 컨텍스트
|
|
|
|
- 표준선: surface-specific anti-corruption adapter + existing provider service capability + transition별 single owner. Operation-specific ingress 함수는 필요한 경로에만 추출하고 물리 package 이동은 의존 방향을 검증하는 최소 범위로 제한한다.
|
|
- 후속 SDD: 없음
|