iop/agent-spec/input/openai-compatible-surface.md
toki 0ffcb88db0 feat: provider-resource-admission-ownership alignment
- Archive provider-resource-admission-ownership milestone/SDD
- Align contract: CP-edge wire, runtime refresh, node runtime, OpenAI surface
- Update roadmap: phase state, priority queue
- Update specs: control-plane ops, OpenAI surface, edge execution, provider pool refresh
- Add node runtime supervisor bootstrapping and unit tests
- Fix control-plane edge registry handler and http_views
- Fix edge model queue admission and long context queue tests
2026-07-22 20:45:04 +09:00

16 KiB

spec_doc_type spec_id status source_evidence
spec input/openai-compatible-surface 부분
type path notes
contract agent-contract/outer/openai-compatible-api.md OpenAI-compatible 외부 HTTP 계약
type path notes
code apps/edge/internal/openai/routes.go OpenAI-compatible route와 bearer auth 처리
type path notes
code apps/edge/internal/openai/chat_handler.go Chat Completions request validation, route dispatch, tool/reasoning 정책
type path notes
code apps/edge/internal/openai/normalized_sse.go normalized Chat Completions SSE stream과 terminal event 처리
type path notes
code apps/edge/internal/openai/provider_tunnel.go provider raw tunnel status/header/body passthrough 처리
type path notes
code apps/edge/internal/openai/responses_handler.go Responses API request validation, metadata/workspace 처리, non-stream completion
type path notes
code apps/edge/internal/openai/run_result.go RunEvent stream을 OpenAI-compatible result로 수집
type path notes
code apps/edge/internal/openai/principal.go principal token hash auth와 authenticated principal metadata 구성
type path notes
code apps/edge/internal/openai/usage_metrics.go OpenAI-compatible request/token/reasoning Prometheus metric emit
type path notes
test apps/edge/internal/openai/chat_handler_test.go Chat Completions route와 target dispatch 검증
type path notes
test apps/edge/internal/openai/provider_tunnel_test.go provider-pool raw tunnel passthrough 검증
type path notes
test apps/edge/internal/service/model_queue_admission_test.go 공유 provider cross-model capacity와 no-candidate unavailable 검증
type path notes
test apps/edge/internal/openai/workspace_metadata_test.go workspace와 metadata 전달 검증
type path notes
test apps/edge/internal/openai/usage_metrics_test.go usage metric label, token breakdown, passthrough usage regression 검증
type path notes
docs docs/openai-usage-grafana.md Grafana query, daily/monthly rollup, usage origin, cloud-equivalent cost, avoided-cost ROI 조회 가이드

스펙: OpenAI-Compatible 입력 표면

목적

Edge가 OpenAI-compatible HTTP 요청을 받아 내부 adapter + target 실행으로 넘기는 현재 동작을 설명한다.

기능 목록

기능 설명
OpenAI-compatible HTTP server openai.enabled=true이면 Edge input manager가 /healthz, /v1/models, /v1/chat/completions, /v1/responses, /api/ route를 제공한다.
bearer auth openai.bearer_token이 있으면 matching bearer authorization header를 요구한다.
principal token auth openai.principal_tokens[]가 설정된 경우 raw token의 SHA-256 hash를 token_hash_sha256과 매칭하고, 매칭 시 iop_principal_ref, iop_principal_alias, iop_token_ref, iop_principal_source metadata를 채운다.
multi-token principal 같은 principal_ref에 여러 token_ref를 연결할 수 있으며, 사용량 metric은 사용자 합산과 token/app별 breakdown을 모두 가능하게 한다.
provider auth forwarding openai.provider_auth가 활성화된 provider tunnel route는 caller의 configured request header에서 raw provider token을 읽어 provider request header로 전달하고, required header가 없으면 dispatch 전에 거부한다.
model catalog /v1/models는 provider-pool models[], legacy openai.model_routes[], openai.models 또는 openai.target 순서로 노출 모델을 만든다.
model dispatch request model은 provider-pool catalog, legacy model route, single target fallback 순서로 해석된다.
provider-pool handoff provider-pool catalog에 model이 있으면 service 요청은 ProviderPool=true로 전달되고 adapter/target은 provider selection 이후 확정된다.
cross-model provider admission 서로 다른 외부 model key가 같은 provider id를 참조하면 Edge의 provider resource lease 하나에서 일반·long capacity를 합산한다.
provider-pool queue/unavailable root provider-pool queue policy를 모든 model group에 공통 적용하고, pending request의 live candidate가 모두 사라지면 timeout을 기다리지 않고 기존 502 node_dispatch_error envelope로 종료한다.
mixed provider dispatch model group 안에 OpenAI-compatible provider와 Ollama/CLI/native provider가 함께 있어도 request field가 아니라 selected provider capability가 passthrough 또는 normalized 실행 경로를 결정한다.
legacy route 변환 legacy route는 외부 model을 route entry의 adapter, target, node, session_id, queue policy로 변환한다.
metadata/workspace 처리 metadata.workspaceRunRequest.workspace로 분리하고, 일반 metadata는 최대 16개 string key/value만 허용한다.
Chat Completions /v1/chat/completions는 non-streaming과 streaming SSE를 지원한다.
model-driven response path request model이 가리키는 provider capability가 provider raw tunnel 또는 normalized RunEvent path를 결정한다. caller metadata는 route나 response shape를 선택하지 않는다.
provider raw passthrough passthrough는 provider status/header/body bytes를 기존 Edge-Node tunnel로 relay하고 pure response body에 IOP 확장 envelope를 섞지 않는다.
provider-native field 보존 provider raw tunnel route는 model served target rewrite와 auth/header 처리 외에 selected provider가 지원하는 OpenAI-compatible 표준 field와 provider extension field를 보존한다.
OpenAI usage metering Edge는 OpenAI-compatible request terminal status와 provider-reported input, output, reasoning, cached_input token usage를 Prometheus counter로 집계한다.
reasoning observation metric provider가 reasoning token을 보고하지 않고 reasoning text만 관측되면 관측 횟수와 character count 보조 metric을 emit하고, 별도 estimated-token counter(iop_openai_reasoning_estimated_tokens_total)로 estimation_method="chars_div_4" 추정을 제공한다.
Grafana usage surface 1차 조회 표면은 Prometheus/Grafana query guide이며 daily/monthly rollup, usage origin breakdown, operator-managed cloud price baseline, cloud-equivalent cost, avoided-cost ROI 기준을 문서로 제공한다. Control Plane/Client dashboard와 request-level ledger는 후속 범위다.
Responses API normalized(non-provider) /v1/responses는 string input의 non-streaming 요청만 지원한다. provider model group route는 /v1/responses를 raw passthrough로 provider POST /v1/responses에 전달한다.
Responses provider passthrough provider-pool model group route와 direct OpenAI-compatible provider route의 /v1/responses는 provider raw tunnel을 사용한다. Edge는 model만 served target으로 rewrite하고 unknown/Codex field와 stream:true raw SSE를 provider로 relay한다. usage metric은 endpoint=responses, response_mode=passthrough, model_group=request alias로 집계한다.
strict output strict output이 켜져 있으면 XML completion contract 기반 instruction 또는 prompt prefix를 추가할 수 있다.
tool call 처리 Chat Completions tools는 provider native metadata 복원 또는 text tool-call synthesis/validation 경로를 사용한다.
cancel 전파 HTTP caller timeout/cancel이 cancel-worthy error이면 Node CancelRun으로 전파한다.

범위

  • 포함: OpenAI-compatible HTTP auth, request validation, route resolution, metadata/workspace 처리, chat/responses 변환, provider-pool dispatch handoff, tool/reasoning/strict output 처리.
  • 제외: OpenAI 원문 API 전체 호환, legacy /v1/completions, A2A JSON-RPC, Node adapter별 provider HTTP 세부, Control Plane 운영 API.

주요 흐름

sequenceDiagram
  participant Caller
  participant OpenAI as OpenAI handler
  participant Service as Edge service
  participant Runtime as Edge-Node runtime

  Caller->>OpenAI: chat/responses request(model)
  OpenAI->>OpenAI: auth, metadata, route 검증
  alt selected provider supports OpenAI-compatible passthrough
    OpenAI->>Service: SubmitProviderTunnel(ProviderPool/direct)
    Service->>Runtime: ProviderTunnelRequest
    Runtime-->>Service: ProviderTunnelFrame stream
    Service-->>OpenAI: tunnel frames
    OpenAI-->>Caller: provider status/header/body bytes
  else selected provider uses normalized execution
    OpenAI->>Service: SubmitRun(adapter/target or ProviderPool)
    Service->>Runtime: RunRequest
    Runtime-->>Service: RunEvent stream
    Service-->>OpenAI: run stream
    OpenAI-->>Caller: OpenAI-compatible response or SSE
  end

계약

  • iop.openai-compatible-api: agent-contract/outer/openai-compatible-api.md
  • 내부 실행 wire: agent-contract/inner/edge-node-runtime-wire.md
  • config/provider pool: agent-contract/inner/edge-config-runtime-refresh.md

설정/데이터/이벤트

  • configs/edge.yamlopenai 섹션이 listener, bearer token, legacy adapter/target, model routes, strict output을 제공한다.
  • top-level models[]가 있으면 OpenAI model list와 provider-pool dispatch에서 legacy route보다 우선한다.
  • provider-pool model group은 capacity + priority + availability 기준으로 provider candidate를 먼저 선택하고, 선택된 provider가 OpenAI-compatible 호출 방식을 지원하면 raw tunnel passthrough로 dispatch한다. Ollama/CLI/native provider가 선택되면 normalized RunRequest path로 dispatch한다.
  • provider capacity와 long-context slot은 model alias별이 아니라 node_id + provider_id별로 공유한다. queue pending 상한과 timeout은 Edge root provider_pool policy이며, lease 반환·refresh·disconnect/reconnect가 모든 model group waiter를 global enqueue 순서로 재평가한다.
  • provider가 full이면 queue policy에 따라 대기하지만 live candidate가 모두 사라지면 즉시 unavailable로 수렴한다. Chat Completions와 Responses provider-pool 표면은 새 public status/field 없이 HTTP 502 node_dispatch_error를 유지한다.
  • openai.provider_auth는 provider tunnel forwarding rule만 저장하고 raw provider token 값은 request-time header에서만 읽는다. inbound IOP Authorization header를 provider token source로 재사용하지 않는다.
  • OpenAI request의 metadata.workspace는 absolute path가 필요한 route에서만 필수 검증된다.
  • Chat Completions와 Responses request는 caller metadata로 provider raw tunnel과 normalized response shape를 선택하지 않는다. route/provider capability만 실행 경로를 결정한다.
  • run metadata에는 openai_model, openai_stream, strict_output, estimated_input_tokens, context_class가 들어갈 수 있다.
  • provider tunnel metadata에는 routing context와 관측 후보가 들어갈 수 있으며, provider body에는 합쳐지지 않는다.
  • Node complete event metadata의 openai_tool_callsopenai_text_tool_fallback은 response tool call 복원에 쓰인다.
  • usage metric은 iop_openai_requests_total, iop_openai_usage_tokens_total, iop_openai_reasoning_observed_total, iop_openai_reasoning_chars_total, iop_openai_reasoning_estimated_tokens_total로 emit된다.
  • usage label은 edge_id, principal_ref, principal_alias, token_ref, model_group, endpoint, response_mode, status, usage_source, token_type처럼 낮은 cardinality 값만 사용한다.
  • principal_ref는 사용자/테넌트 참조값이고 token_ref는 앱/통합/용도별 token 참조값이다. 같은 principal에 여러 token이 있으면 principal_ref 기준 합산과 token_ref 기준 분해를 함께 사용할 수 있다.
  • request_id, session_id, raw bearer token, provider token, raw prompt/response는 metric label에 넣지 않는다.
  • provider body usage와 provider tunnel USAGE frame이 모두 있으면 body input/output을 우선하고 proto-only reasoning/cached input을 보조로 병합해 중복 집계를 피한다.

검증

  • go test ./apps/edge/internal/openai
  • go test ./apps/edge/internal/service
  • go test ./apps/edge/internal/openai -run 'Tunnel|UsageMetrics|ToolValidation|Dispatch|Reasoning|Retry'
  • rg --fixed-strings "cloud_equivalent_cost" docs/openai-usage-grafana.md
  • make test-openai-ollama
  • provider별 실제 runtime smoke는 환경별 agent-test/dev 또는 dev-corp profile을 따른다.

한계와 주의사항

  • normalized(non-provider) /v1/responses는 non-streaming string input만 지원한다. provider model group route의 /v1/responses는 raw passthrough로 streaming과 Codex/unknown field를 그대로 provider에 전달한다.
  • /v1/completions는 제공하지 않는다.
  • OpenAI-compatible request에 provider/Ollama 전용 root field를 추가하지 않는다.
  • workspace는 prompt 본문에 섞지 않고 metadata에서 분리한다.
  • pure passthrough body는 provider-original byte stream이며 IOP 확장 envelope나 normalized label을 포함하지 않는다.
  • provider route와 non-provider normalized route의 차이는 selected provider capability에서 파생되며 caller metadata selector로 고르지 않는다.
  • text tool-call synthesis는 요청 tools[] schema를 기준으로만 수행한다. 자연어 추론으로 tool call을 만들지 않는다.
  • private token이나 endpoint 원문은 tracked spec/docs에 남기지 않는다.
  • metadata.user는 identity source가 아니며 사용되지 않는다.
  • caller가 metadata.iop_principal_*를 보내도 authenticated context 값이 overwrite한다.
  • openai.principal_tokens[] 변경은 restart-required로 분류된다.
  • principal token auth가 실패하면 legacy openai.bearer_token이 unmapped fallback으로 동작한다.
  • provider가 별도 reasoning token을 보고하지 않으면 provider-reported token_type="reasoning"은 증가하지 않고, 별도 estimated token counter(iop_openai_reasoning_estimated_tokens_total, estimation_method="chars_div_4")로 ceil(chars/4) 추정을 제공하되 billing-grade 확정값이 아니다.
  • Grafana guide는 metric 조회와 operator-managed price baseline 예시이며 live cloud pricing, billing, chargeback, long-term ledger, 사용자별 제한 enforcement의 source of truth가 아니다.
  • Seulgivibe Claude/OpenAI proxy는 별도 OpenAI-compatible provider family label로 보존될 수 있지만, HTTP body shape는 provider tunnel passthrough 경계를 따른다.

변경 기록

  • 2026-07-07: 현재 코드와 OpenAI-compatible 계약 기준으로 bootstrap spec 작성.
  • 2026-07-07: 기능 목록 중심으로 축소하고 주요 흐름을 Mermaid sequence diagram으로 정리.
  • 2026-07-08: Chat Completions provider raw tunnel과 normalized execution semantics를 현재 코드와 계약 기준으로 반영.
  • 2026-07-10: principal token 기반 usage metering, Prometheus metric, Grafana query guide, reasoning/cached token breakdown을 Milestone completion evidence 기준으로 반영.
  • 2026-07-10: 일별 Usage 비용/ROI 리포트 MVP 종료 검토에서 daily/monthly rollup, usage origin breakdown, cloud-equivalent cost, avoided-cost ROI 문서 표면을 반영.
  • 2026-07-11: provider model group /v1/responses raw passthrough 동작(모델 rewrite, unknown/Codex field 보존, streaming relay, provider auth forwarding)과 endpoint=responses usage metric label을 현재 코드 기준으로 반영.
  • 2026-07-11: Responses provider route의 provider raw tunnel 동작을 반영했다.
  • 2026-07-11: provider auth forwarding과 Seulgivibe OpenAI-compatible provider family surface를 종료 검토 기준으로 보강.
  • 2026-07-12: Model Group Mixed Provider Dispatch 종료 검토 기준으로 selected provider capability 기반 passthrough/normalized 실행 경로를 반영.
  • 2026-07-14: OpenAI-compatible Provider Passthrough 계약 동기화 종료 검토 기준으로 caller-facing response selector 설명을 제거하고, model 기반 route와 provider-native field 보존 기준을 반영.
  • 2026-07-14: provider 미보고 reasoning text에 estimated-token counter(iop_openai_reasoning_estimated_tokens_total, estimation_method="chars_div_4") 추가와 provider-reported reasoning 우선 기준을 반영.
  • 2026-07-18: 저장소 구조 분해 뒤 streaming, provider tunnel, split test의 source_evidence를 현재 경로로 동기화.
  • 2026-07-22: cross-model provider resource admission, provider-pool 공통 queue policy와 live candidate 소진 시 502 unavailable 의미를 현재 service/OpenAI 구현과 계약 기준으로 동기화.